Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GHCi ignores type signature

Tags:

Prelude> let myprint = putStrLn . show
Prelude> :t myprint
myprint :: () -> IO ()

OK, nothing too unusual here. Just GHCi type defaulting rules, I guess...

Prelude> let myprint = (putStrLn . show) :: Show x => x -> IO ()
Prelude> :t myprint
myprint :: () -> IO ()

What sorcery is this?? You're point-blank ignoring my type declaration?! O_O

Is there some way I can convince GHCi to do what I actually intended?

like image 965
MathematicalOrchid Avatar asked Mar 22 '15 10:03

MathematicalOrchid


People also ask

Which of the following is not a valid Haskell type signature?

(:==) is not a valid symbol for a function or variable identifier in Haskell.

How do I load a Haskell file into Ghci?

Open a command window and navigate to the directory where you want to keep your Haskell source files. Run Haskell by typing ghci or ghci MyFile. hs. (The "i" in "GHCi" stands for "interactive", as opposed to compiling and producing an executable file.)

How do I check my Ghci version?

The version number of your copy of GHC can be found by invoking ghc with the --version flag (see Verbosity options). The compiler version can be tested within compiled code with the MIN_VERSION_GLASGOW_HASKELL CPP macro (defined only when CPP is used).


2 Answers

Adding a type annotation to an expression as in

e :: type

makes the compiler check that e has that type, as well as use that type to drive type variables instantiation and instance selection. However, if the type is polymorphic it can still be instantiated later on. Consider e.g.

(id :: a -> a) "hello"

Above, a will be instantiated to String, despite my annotation. Further,

foo :: Int -> Int
foo = (id :: a -> a)

will make a to be instantiated to Int later on. The above id annotation does not give any information to GHC: it already knows that id has that type. We could remove it without affecting the type checking at all. That is, the expressions id and id :: a->a are not only dynamically equivalent, but also statically such.

Similarly, the expressions

putStrLn . show

and

(putStrLn . show) :: Show x => x -> IO ()

are statically equivalent: we are just annotating the code with the type GHC can infer. In other words, we are not providing any information to GHC it does not already know.

After the annotation is type checked, GHC can then instantiate x further. The monomorphism restriction does that in your example. To prevent that, use an annotation for the binding you are introducing, not for the expression:

myprint :: Show x => x -> IO ()
myprint = (putStrLn . show)
like image 93
chi Avatar answered Sep 29 '22 20:09

chi


We can do the following, with monomorphism restriction on:

>let myprint :: Show x => x -> IO (); myprint = putStrLn . show
>:t myprint
myprint :: Show x => x -> IO ()

This is not the same as let myprint = putStrLn . show :: Show x => x -> IO (). In the former case we have a binding with a type signature, in the latter case we a have a let binding with a type annotation inside the right hand side. Monomorphism checks top-level type signatures, but not local annotations.

like image 37
András Kovács Avatar answered Oct 03 '22 20:10

András Kovács