0

I want to write a function mapsucc that produces the successor of every item in a list. Easy enough:

    mapsucc xs = map succ xs

works perfectly. I think I could use Currying to make this definition even shorter:

    mapsucc = map succ

but I get:

No instance for (Enum b0) arising from a use of ‘succ’
The type variable ‘b0’ is ambiguous
Relevant bindings include
  mapsucc :: [b0] -> [b0] (bound at mapsucc.hs:1:1)
Note: there are several potential instances:
  instance Enum Ordering -- Defined in ‘GHC.Enum’
  instance Enum Integer -- Defined in ‘GHC.Enum’
  instance Enum () -- Defined in ‘GHC.Enum’
  ...plus six others
In the first argument of ‘map’, namely ‘succ’
In the expression: map succ
In an equation for ‘mapsucc’: mapsucc = map succ

Now, it's not clear to me why the curried version should be ambiguous, and why, instead, the one with explicit parameters is not.

Also, if from ghci I declare mapsucc with a let, it works perfectly:

Prelude> let mapsucc = map succ
Prelude> mapsucc "hello"
"ifmmp"

Same behavior if I redefine succ:

mysucc x = succ x

works perfectly, while

mysucc = succ

gives the same error:

No instance for (Enum a0) arising from a use of ‘succ’
The type variable ‘a0’ is ambiguous
Relevant bindings include
  mysucc :: a0 -> a0 (bound at mapsucc.hs:3:1)
Note: there are several potential instances:
  instance Enum Ordering -- Defined in ‘GHC.Enum’
  instance Enum Integer -- Defined in ‘GHC.Enum’
  instance Enum () -- Defined in ‘GHC.Enum’
  ...plus six others
In the expression: succ
In an equation for ‘mysucc’: mysucc = succ

and, again, if I define mysucc in ghci throug a let, it works perfectly:

Prelude> let mysucc = succ
Prelude> mysucc 3
4
duplode
  • 33,731
  • 7
  • 79
  • 150
Archangel
  • 83
  • 7

2 Answers2

5

Like many before you, you have been caught out by the Dreaded Monomorphism Restriction. The rule for this is roughly as follows:

  • If the value has explicit arguments then it is automatically polymorphic in the way that you expect. This is why your first version works: it has the type

mapsucc :: Enum b => [b] -> [b]

  • In ghc (but not ghci), if the value has no explicit arguments (like your second version) then it is not polymorphic. So ghc tries to figure out what type b is and discovers that it can't. Hence the error message.

  • In ghci there is no monomorphism restriction, so your second example works there and it has the same type as the first.

For more details, including an explanation of why this rule exists, see here.

Paul Johnson
  • 17,438
  • 3
  • 42
  • 59
3

You're running into the so called monomorphism restriction.

Newer GHC versions give a slightly better error message:

test.hs:1:9: error:
    • Ambiguous type variable ‘b0’ arising from a use of ‘succ’
      prevents the constraint ‘(Enum b0)’ from being solved.
      Relevant bindings include f :: [b0] -> [b0] (bound at test.hs:1:1)
      Probable fix: use a type annotation to specify what ‘b0’ should be.
      These potential instances exist:
        instance Enum Ordering -- Defined in ‘GHC.Enum’
        instance Enum Integer -- Defined in ‘GHC.Enum’
        instance Enum () -- Defined in ‘GHC.Enum’
        ...plus six others
        ...plus two instances involving out-of-scope types
        (use -fprint-potential-instances to see them all)
    • In the first argument of ‘map’, namely ‘succ’
      In the expression: map succ
      In an equation for ‘f’: f = map succ
  |
1 | f = map succ
  |         ^^^^

As you can see, here it directly gives the advice to write a type signature which is indeed the right fix here.

Cubic
  • 14,902
  • 5
  • 47
  • 92
  • 1
    It's a little surprising to me that the error messages still don't mention that the monomorphism restriction is triggering them (since this would make it easier to search for). It seems like this would be possible to implement, since it *is* the result of the monomorphism option being turned on... Do you happen to know if there's a reason why this isn't done? I feel like I haven't seen this discussed, actually. – David Young May 17 '18 at 03:57
  • @DavidYoung Not sure. My guess would be that that might encourage beginners to turn off the monomorphism restriction in their source code instead of just adding type signatures to their definitions, but who knows? – Cubic May 17 '18 at 09:03