开发者

Adding type constraints to the context of instance declarations in Haskell

开发者 https://www.devze.com 2023-01-25 08:06 出处:网络
I am trying to represent weighted edges. I eventually want to have OutE to be an instance of Eq and Ord, with the constraint that etype is an instance of Eq and Ord. Assume I have following file as te

I am trying to represent weighted edges. I eventually want to have OutE to be an instance of Eq and Ord, with the constraint that etype is an instance of Eq and Ord. Assume I have following file as temp.hs:

data (Ord etype)=> OutE vtype etype = OutE {destVertex:: vtype, edgeValue::etype}

applyFunBy accessor ordfun = (\x y -> (ordfun (accessor x) (accessor y)))

instance Eq (OutE vtype etype) where
    --(==) :: Ord etype => (OutE vtype etype) -> (OutE vtype etype) -> Bool
    --(/=) :: Ord etype => (OutE vtype etype) -> (OutE vtype etype) -> Bool
    (==) = applyFunBy edgeValue (==)
    (/=) = applyFunBy edgeValue (/=)

when I load this in ghci, I get the following errors:

temp.hs:10:19:
    Could not deduce (Ord etype)
      from the context (Eq (OutE vtype etype))
      aris开发者_运维技巧ing from a use of `edgeValue' at temp.hs:10:19-27
    Possible fix:
      add (Ord etype) to the context of the instance declaration
    In the first argument of `applyFunBy', namely `edgeValue'
    In the expression: applyFunBy edgeValue (==)
    In the definition of `==': == = applyFunBy edgeValue (==)

temp.hs:11:19:
    Could not deduce (Ord etype)
      from the context (Eq (OutE vtype etype))
      arising from a use of `edgeValue' at temp.hs:11:19-27
    Possible fix:
      add (Ord etype) to the context of the instance declaration
    In the first argument of `applyFunBy', namely `edgeValue'
    In the expression: applyFunBy edgeValue (/=)
    In the definition of `/=': /= = applyFunBy edgeValue (/=)
Failed, modules loaded: none.

If include the lines for the type signatures for (==) and (\=), I get:

temp.hs:6:1:
    Misplaced type signature:
    == ::
      (Ord etype) => (OutE vtype etype) -> (OutE vtype etype) -> Bool

temp.hs:7:1:
    Misplaced type signature:
    /= ::
      (Ord etype) => (OutE vtype etype) -> (OutE vtype etype) -> Bool


You limited etype to be Ord in the defintion of OutE:

data (Ord etype) => OutE vtype etype = ...

But in the Eq instance, you're actually trying to define the instance for any etype unrestrictedly.

instance Eq (OutE vtype etype) where

Of course this doesn't work since OutE itself is just defined for Ord etypes, thus you'll have to add the typeclass constraint to the instance definition as well.

instance (Ord etype) => Eq (OutE vtype etype) where

Note that one definition of either == or /= is sufficient for the typeclass to work.


Note that it's often easier and therefore considered better style not to have typeclass constraints on data-types, but just on instances/methods that actually require the functionality of the typeclass.

In many cases, one does not need the constraint and just ends up with unnecessarily clumsy type signatures.

Take e.g. some ordered map type Ord key => Map key value.

What if we just want to list all keys? Or get the number of elements? We don't need the keys to be Ord for these, so why not just leave the map unrestricted with simple

getKeys :: Map key value -> [key]
getLength :: Map key value -> Int

and just add the typeclass when we really need it in a function like

insert :: Ord key => key -> value -> Map key value


data (Ord etype)=> OutE vtype etype = OutE {destVertex:: vtype, edgeValue::etype}

First issue: This is consdered bad style. Your data type declarations shouldn't have constraints. Leave the constraints to the functions, much like the containers package does.

instance Eq (OutE vtype etype) where

Second "issue". You can just add deriving (Eq) after your data declaration. I'm guessing you know that and are writing the instance explicitly for your own learning (good for you)...

instance Eq (OutE vtype etype) where
    (==) = applyFunBy edgeValue (==)
    (/=) = applyFunBy edgeValue (/=)

Third issue: you can't compare values for equity if they are of the Eq class. So you want to say etype is constrained by Eq:

instance (Eq etype) => Eq (OutE vtype etype) where
    (==) = applyFunBy edgeValue (==)
    (/=) = applyFunBy edgeValue (/=)

Fourth, you don't actually need to write an instance for both (==) and (/=). The defaults will work once you define one of these.

0

精彩评论

暂无评论...
验证码 换一张
取 消