The following program type checks if I specify it on the command line (e.g. ghci file.hs
):
import Data.Ratio
foo = let x = [1..]
y = (1%2) + (head x)
in y
However, if I enter it interactively I'll get a type error:
Prelude> import Data.Ratio
Prelude Data.Ratio> let x = [1..]
Prelude Data.Ratio> let y = (1%2) + (head x)
<interactive>:1:23:
Couldn't match expected type `Ratio a0' with actual type `Integer'
It seems that x
is being eagerly typed as [Integer]
as opposed to the more general (Num t, Enum t) => [t]
.
Is there anything I can do about that? Are there other situations where interactive mode will differ from batch mode?
Bindings without arguments, i.e those of the form x = ...
are subject to the monomorphism restriction, which means that GHC will try to make it non-polymorphic using whatever type information is available, and falling back on type defaulting to resolve any ambiguities. (Actually, GHCi uses a slightly more permissive set of defaulting rules, but that doesn't really matter for this question).
Since there is no other type information available when you write let x = [1..]
, type defaulting causes the type to be inferred as [Integer]
, since Integer
is the default numeric type.
There are several ways to solve this problem:
Use a type signature. This always works, but it can sometimes be tedious when working with complex types.
> let x = [1..] :: [Rational]
Write the binding with arguments. This doesn't apply in your case, but you sometimes see this problem when writing point-free function definitions.
> let f = (+)
> :t f
f :: Integer -> Integer -> Integer
> let f x y = x + y
> :t f
f :: Num a => a -> a -> a
Give the type checker more information. In your case we could avoid the problem by writing both bindings in one let
statement. GHC can then use the type information from the second binding to correctly infer that x
should have the type [Rational]
.
> let x = [1..]; y = 1%2 + head x
> :t x
x :: [Ratio Integer]
Disable the monomorphism restriction. This can have serious performance implications if you were expecting the type of something to be e.g. an Integer
, while it is actually a Num a => a
, since the latter must be recomputed each time while the former can be shared. This is the main reason why the restriction exists in the first place.
However, in the interpreter this is typically less of an issue, so the convenience is often worth it.
> :set -XNoMonomorphismRestriction
> let x = [1..]
> :t x
x :: (Num t, Enum t) => [t]
If you want this on by default, you can add it to your .ghci
file.
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