Reputation: 4402
I'm not really sure how to phrase the title better. Anyways, say I do something like this
(a, _) = random (mkStdGen 0)
b = if a then 1 else 0
Haskell can deduce that the type of a
is Bool
.
But if I do
(a, _) = random (mkStdGen 0)
b = if a > 0.5 then 1 else 0
it doesn't figure out that I want a
to be a Float
.
I don't know much about type inference and I don't know much about the typesystem but couldn't it just look for a type that is Random
, Ord
, and Fractional
?
Float
would fit in there.
Or why not just have a more generic type in that place since I obviously only care that it has those three typeclasses? Then later if I use the value for something else, it can get more info and maybe get a more concrete type.
Upvotes: 1
Views: 66
Reputation: 18189
I see the other answers haven't mentioned Haskell's defaulting rules. Under many circumstances, Haskell by default will choose either Integer
or Double
(not Float
, though) if your type is ambiguous, needs to be made specific, and one of those fits.
Unfortunately, one of the requirements for defaulting is that all the type classes involved are "standard". And the class Random
is not a standard class. (It used to be in the Haskell 98 Report, but was removed from Haskell 2010. I vaguely recall it did not work for defaulting even back then, though.)
However, as that link also shows, you can relax that restriction with GHC's ExtendedDefaultRules
extension, after which your code will cause Double
to be chosen. You can even get Float
to be chosen if you make a different default
declaration that prefers Float
to Double
(I would not generally recommend this, though, as Double
is higher precision.) The following works the way you expected your original to do:
{-# LANGUAGE ExtendedDefaultRules #-}
import System.Random
default (Integer, Float)
(a, _) = random (mkStdGen 0)
b = if a > 0.5 then 1 else 0
Upvotes: 1
Reputation: 7350
You can always just do this to solve your problem:
(a, _) = random (mkStdGen 0) :: (Float, StdGen)
This forces a
to be of type Float
and the compiler is able to infer the rest of the types.
The problem Haskell has more than one type which is in the classes Random
, Ord
and Fractional
. It could be Float
, as well as it could also be Double
. Because Haskell does not know what you mean, it throws an error.
Upvotes: 1
Reputation: 54058
The reason why Haskell can't figure this out is because numeric literals are polymorphic, so if you see 0.5
, the compiler interprets it more or less as 0.5 :: Fractional a => a
. Since this is still polymorphic, even with Random
and Ord
constraints, the compiler does not "guess" what you meant there, it instead throws an error saying that it doesn't know what you meant. For example, it could also be Double
, or a custom defined data type, or any one of a potentially infinite number of types. It's also not very difficult to specify a type signature here or there, if a > (0.5 :: Float) then 1 else 0
isn't much extra work, and it makes your intention explicit rather than implicit.
It also can't just use a "generic type in that place", even with those three typeclasses, because it doesn't know the behavior of those typeclasses without a concrete type. For example, I could write a newtype around Float
as
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
newtype MyFloat = MyFloat Float deriving (Eq, Show, Read, Num, Floating, Random)
instance Ord MyFloat where
compare (MyFloat x) (MyFloat y) = compare (sin x) (sin y)
This is perfectly valid code, and could be something that satisfies your constraints, but would have drastically different behavior at runtime than simply Float
.
Upvotes: 1