3

I have the following Haskell definitions in a source file

nNotes=7

pitchLimit k = k < 0 || k > 20  
pitcherror = error "Invalid Pitch"

getOctave k
    | pitchLimit k = pitcherror
    | otherwise = k `div` nNotes

I do not declare the type of getOctave. Now here is my issue regarding types. Later on in the same source file, I used getOctave with an Int. I load the source file into GHCI and Haskell infers that the type of getOctave is Int->Int. Now I comment out the code that uses getOctave as an Int. I reload the source file and now Haskell infers getOctave to be of type Integer->Integer. Seems to me now that the default of div is Integer->Integer. Now I uncomment that code and specify the type of getOctave to be (Integral a) => a ->a. And Haskell reports an error. What's wrong with using (Integral a) => a -> a.

Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
johnson
  • 283
  • 2
  • 9
  • 2
    Probably a consequence of the monomorphism restriction. Non function bindings like `x=4` are only given monomorphic types if not explicitly annotated. https://stackoverflow.com/questions/32496864/what-is-the-monomorphism-restriction – chi Aug 28 '17 at 07:27

2 Answers2

3

When you use a literal like 7, GHC tries to guess which type you meant. You can reproduce this with a .hs file that (apart from a module declaration) contains only

nNotes = 7

This will give you the compiler warning:

* Defaulting the following constraint to type `Integer'
    Num t0 arising from the literal `7'
* In the expression: 7
  In an equation for `nNotes': nNotes = 7

From there, the compiler will infer all the other types.

You can generalise your code by explicitly annotate nNotes with a type, like this:

nNotes :: Num a => a
nNotes = 7

Once you do that, your functions get these automatically inferred types:

λ: :type pitchLimit
pitchLimit :: (Ord a, Num a) => a -> Bool
λ: :type getOctave
getOctave :: Integral t => t -> t

As you can see, getOctave can take any Integral as input, including both Integer and Int.

The reason that getOctave infers t to be of the class Integral is because div is defined for Integral.

Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
1

I believe the problem here is you have to also declare nNotes to be of type nNotes :: Integral a => a.

Andrew Lei
  • 335
  • 2
  • 9
  • That's interesting. I forgot about nNotes. Haskell infers the type of it to be `Int`. Why? – johnson Aug 28 '17 at 06:01
  • @johnson When I load the source file in ghci, it looks like it assumes nNotes is `Integer`. When you had something else of type `Int` in the code, the compiler probably realised nNotes had to be `Int`. My guess is that, unless contradicted, the compiler assumes integer literal definitions should be `Integer`. In which case it might be a bug with GHC if it can't automatically infer that you want nNotes to be of type `Integral a => a` (but I don't know much about compilers). – Andrew Lei Aug 28 '17 at 06:11