An error with Haskell classes I fall all the time and can't understand
-
21-09-2019 - |
문제
there's an error I come across all the time but can't understand how to make it right. An example of code that gives me this error is:
class Someclass a where
somefunc :: (Num b) => b -> a -> a
data Sometype = Somecons Int
instance Someclass Sometype where
somefunc x (Somecons y) = Somecons (x+y)
The error message is:
Couldn't match expected type 'b' against inferred type 'Int'
'b' is a rigid type variable bound by the type signature for 'somefunc' at error.hs:3:21
In the second argument of '(+)', namely 'y'
In the first argument of 'Somecons', namely '(x + y)'
In the expression: Somecons (x + y)
I understand that the error message is trying to tell me that I used a name of type Int where he was expecting something with type (Num b) => b. What I can't understand is that Int fits in (Num b)=>b. Shouldn't the compiler understand what I'm telling him (that for this specific instance b should be an integer? How can I make this fit?
Coment: Of course in this specific example I could have made somefunc with type signature:
somefunc :: a -> a-> a
but supose I wanted something like:
data Newtype = Newcons (Int, Int)
instance Someclass Newtype where
somefunc x (Newtype (y,z) ) = Newtype (y+x, z)
Things like that recurrently happens when I'm trying to do something in haskell.
해결책
Well, you can make the point clearer when thinking of the generics notation using universal quantification.
somefunc :: (Num b) => b -> a -> a
therefore means nothing but
somefunc :: forall a b . Num b => b -> a -> a
This means your class function must be defined for any numeric b
.
The code
Data Sometype = Somecons Int
instance Someclass Sometype where
somefunc x (Somecons y) = Somecons (x+y)
forces b
to have one concrete type - Int
, which doesn't conform with the requirement to work for any numeric type.
You might want to have something like this
class Num b => SomeClass a b where
somefunc :: b -> a -> a
instance Someclass Somecons Int where
-- ...
다른 팁
The problem can be seen in the signature of the +
operator:
(+) :: Num a => a -> a -> a
Because of this, when you use +
in somefunc
with an Int
, it forces b to be an Int
, and therefore somefunc
becomes:
somefunc :: Int -> Sometype -> Sometype
To implement the Someclass
class, somefunc
is expected to have this signature:
somefunc :: Num b => b -> Sometype -> Sometype
That is, it should work with any type that is an instance of Num
. Your function only works with Int
s.
You cannot mix types, as (+) :: a → a → a
let x = 1.2::Double; y=2::Int in x + y
This will fail already.
Num is too general, if you specified that x::Double
you could get it working by an explicit 'typecast' (fromIntegral
)
instance Someclass Sometype where
somefunc x (Somecons y) = Somecons (x + (fromIntegral y))
I think you want something like this
instance Someclass Sometype where
somefunc :: Int → Sometype → Int
somefunc x (Somecons y) = Somecons (x + y)
btw, you need to type data instead of Data :-)
To continue on Dario's example, what you seem to be asking for is:
class Someclass a where
somefunc :: exists b . (Num b) => b -> a -> a
That is, instead of the "you pick a type, and my function will work" promise that "forall b . Num b => b"
implies, you want the "I'll pick a type, so my function will work" promise of "exists b . (Num b) => b"
, which Sr. Fernandes mentioned.
More importantly, you aren't showing how your constraint (Num b) => b
helps your case.
The really interestingsituation would be: how should you class handle the following type:
data BMephType i o = BMT (i -> (o, BMephType i o))
instance Someclass (BMephType (Complex Double -> String) String) where
Most likely, your solution will involve a Complex Double
. Somewhere. If it only involves a Complex Double
, and no other Num
types, then you're looking for an existential type, not a universal.