0

I'm new to haskell and struggling with type signatures. I'm working on a single-function Collatz chain generator. I have seen a few haskell collatz questions, but haven't seen any that answer the kind of problem that I'm trying to answer. I'm using GHCI.

Here is my function:

collatz :: (Num a) => a -> [a]
collatz n 
    | n == 1  = [n]
    | odd n   = n : collatz (n * 3 + 1)
    | even n  = n : collatz (n / 2)

It seems like this should be straightforward; it should just append new transformations to the list. However, the interpreter gives me these errors:

collatz.hs:9:13:
  Could not deduce (Eq a) arising from a use of `=='
  from the context (Num a)
....
collatz.hs:10:11:
  Could not deduce (Integral a) arising from a use of `odd'
  from the context (Num a)
....
collatz.hs:11:36:
  Could not deduce (Fractional a) arising from a use of `/'
  from the context (Num a)

Every combination of type type signatures that the compiler tells me to use seems to fail

collatz :: (Eq a, Num a) => a -> [a]
collatz :: (Integral a) => a -> [a]
collatz :: (Fractional a) => a -> [a]

I'm clearly missing something fundamental. Can anyone tell me how to produce a collatz chain with a single function?

Mark Karavan
  • 2,654
  • 1
  • 18
  • 38
  • Often it is helpful to use concrete types to debug type errors. Replacing `a` with `Int` would give a much simpler message which might make the problem more obvious. – ErikR Dec 17 '14 at 07:50

1 Answers1

3

The problem is, (/) needs a Fractional, like float, whereas, even and odd need Integrals.... Basically, the concept of even and odd don't make sense with fractionals (ie- is 1.5 odd or even?).

Since you know that n is even in the second case, you can replace (/) with quot, and that will fix the problem.

The type will be

collatz :: Integral a => a -> [a]
jamshidh
  • 12,002
  • 17
  • 31
  • ...and you either need to use pattern matching instead of ==0 to relieve yourself of the Eq constraint, or add it explicitly. – AndrewC Dec 17 '14 at 07:10
  • @AndrewC- I would have thought also, but ghc disagrees.... `Integral a` seems to be enough. I have to admit, I am confused, but I am guessing that `Integral a` must imply `Eq a`. – jamshidh Dec 17 '14 at 07:23
  • @AndrewC- yeah, looking through the docs, it seems that Integral requires Num, Num requires Ord, and Ord requires Eq..... – jamshidh Dec 17 '14 at 07:26
  • It's because `class (Eq a, Show a) => Num a` and `class (Num a, Ord a) => Real a` and `class (Real a, Enum a) => Integral a`, so according to Haskell 2010 you can have `| n == 0`. Recent versions of GHC do not have superclasses for `Num`, so they'd need the context or a pattern match. I'd advise folk to use the explicit `Eq` context or pattern match to be compatible with both the standard and current GHC, and presumably future standards. – AndrewC Dec 17 '14 at 17:28
  • @jamshidh- what is the difference between encapsulating the constraint in parens, vs leaving it open? That is, :: Integral a => vs :: (Integral a) =>? Is it just syntactic sugar that is useful when you use multiple constraints? – Mark Karavan Dec 17 '14 at 21:22
  • @MarkKaravan- I don't think it makes a difference for one constraint, but you need it for two or more. – jamshidh Dec 17 '14 at 22:37