toFloat :: (Floating a) => String -> a
toFloat s = read s :: Float
main = print (toFloat "1")
Gives me the error:
Could not deduce (a ~ Float)
from the context (Floating a)
I'm sure I'm missing something basic, but it seems like my toFloat should always return a Float and that Float should imply Floating.
The type signature promises the result will be any instance of the Floating
class the caller wants. The implementation says "Know what? Nevermind on that promise that it can be any type - let's just make it a Float
".
Then the compiler comes along and says "Whoa! You're not returning the type you promised you would." Except that it tried really hard to make your type signature and your implementation match up. It said to itself "If this was constrained somehow, such that a
was always the same thing as Float
, this would be correct." It really wanted to find a way your code was correct. Well, the way to write such a constraint is using ~
, the type equality operator. A constraint of (a ~ Float)
would mean "a
is the same type as Float
". So the compiler checks the context you provided in the type signature, and it fails to find that constraint. And it runs out of ways to make your type signature and implementation work together, gives up, and reports an error.
Unfortunately, the error it reports is a bit opaque due to how much effort it put into trying to make your code work. Just the tiniest change, adding one little constraint, would have made it right. So it reports that that constraint wasn't present. But it doesn't report why it was looking for that constraint, making the whole thing a bit unclear if you haven't seen it before.
This question or something very similar to it gets asked on a regular basis. (That's not a complaint, I'm just pointing out that you're not the only person confused by this.)
In an OO language, you can say "this function returns something that implements X". The function can then return whatever the hell it feels like returning, so long as it does in fact implement X.
Haskell does not work like that. If you say "this function returns something that implements X", then the function must be able to yield any possible type that implements X!
The key difference is this: In an OO language, the function decides what type to return (within the specified constraints). In Haskell, the caller decides what type to return (again, within the stipulated constraints).
Once you comprehend this key difference, the rest is fairly self-evident.
Again, a lot of people seem to misunderstand this part. We should probably mention it more in tutorials and stuff, because it seems to be a VFAQ...
You are saying toFloat
can return any type belonging to Floating
typeclass but you are restricting it to Float
, which is wrong. Your function is polymorphic in a
so you just can not return an instance of Floating
, it should be able to work with all the instances.
Otherway you can understand this by
toFloat :: (Read a,Floating a) => String -> a
toFloat s = read s
In ghci
*Main> :t toFloat "12.1"
toFloat "12.1" :: (Floating a, Read a) => a
*Main> :t (toFloat "12.1" :: Float)
(toFloat "12.1" :: Float) :: Float
*Main> :t (toFloat "12.1" :: Double)
(toFloat "12.1" :: Double) :: Double
Since it returns type belonging to typeclass Floating
you should be able to convert it to any type (belonging to Floating
) by providing explicit type signatures after function is applied.
On the other hand remember your case when you are explicitly returning Float
now you can not just say I expect Double
from this function because that can not happen without explicit conversion.
Another way to understand how horrible your assumption is consider function read
read :: Read a => String -> a
Now here according to you, you can just return say Int
for everything because Int
has an instance for Read
. Now you can understand what will happen if you do something like
read "12" + (1.2 :: Double)
This is as simple as you could have it:
-- The simplest.
toFloat :: String -> Float
toFloat = read
-- More generalized.
toFloat' :: (Floating a, Read a) => String -> a
toFloat' = read
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With