0

I have defined the following function

let repl x n = [x | _ <- [1..n]]

which imitates the built-in replicate function.

While experimenting with it, I noticed a strange thing: repl 10 0 evaluates to [], while repl 10 -1 produces an error:

No instance for (Show (t10 -> [t0])) arising from a use of ‘print’
In a stmt of an interactive GHCi command: print it

On the other hand, both [1 .. 0] and [1 .. -1] evaluate to [] without producing any errors.

Moreover, both [42 | _ <- [1 .. 0]] and [42 | _ <- [1 .. -1]] evaluate to [] without errors.

So why does my function call result in an error where the explicit substitution doesn't? And more importantly, where does the apparent difference between [1 .. 0] and [1 .. -1] stem from?

And a final question: when I write:

repl 42 -1

the error is exactly the same as with repl 10 -1, i.e. it still has the (Show (t10 -> [t0])) bit in it. I was expecting it to have something like ((Show (t42 -> [t0]))). What's this 10?

Armen Tsirunyan
  • 130,161
  • 59
  • 324
  • 434
  • 1
    You may find the answers to these questions helpful: [prefix unary operator](http://stackoverflow.com/questions/3406320/prefix-form-of-unary-operator-in-haskell/3406692#3406692); [why can't I multiply negative numbers without brackets](http://stackoverflow.com/questions/26073878/); [funny haskell behavior with negative arguments](http://stackoverflow.com/questions/14741552/). – Christian Conkle Nov 23 '14 at 16:55

3 Answers3

4

Other answers have pointed out that you need to wrap -1 in parentheses. This is an odd corner of the Haskell 98 spec that jumps out to bite unexpectedly. It's not the case that you can never write a negative number without parentheses: -1 * 5 is fine. It's just that the unary prefix operator doesn't have higher precedence than the binary infix operator, so a - is frequently parsed as the latter. Whitespace around operators is not significant in Haskell.

And the incomprehensible typeclass error doesn't help. Incidentally, t10 and t0 are just placeholder type variables made up by the compiler; I don't think it has anything to do with the actual numeric literals you use. And informally, errors like Could not deduce (Num (a0 -> t)) usually indicate to me that a function is applied to too few arguments.

Alternatively, the (undocumented?) NegativeLiterals language extension in GHC 7.8 changes the meaning of -1 to address this problem.

> :set -XNegativeLiterals
> :t repl 10 -1
repl 10 -1 :: Num t => [t]
Community
  • 1
  • 1
Christian Conkle
  • 5,932
  • 1
  • 28
  • 52
  • 2
    This is not true in this case - function evaluation precedes operators, so `repl 10 -1` is equivalent to `(repl 10) - 1` and not to `repl 9` – Benesh Nov 23 '14 at 16:35
  • 1
    It's generally not a good idea to enable language extensions in Haskell. First, because it makes the code non Haskell98 compatible. Second, because there is no need for it. An extra set of parentheses is not a big problem and it does not warrant the use of a language extension. – Aadit M Shah Nov 23 '14 at 16:36
  • 1
    @AaditMShah - That's kind of an extreme view. Sure, in this case it's easy enough to fix with parentheses. But part of the philosophy of Haskell from the beginning has been to have constant experimentation with the language itself. There are a few extensions that are widely panned, e.g. `IncoherentInstances`, but I haven't heard `NegativeLiterals` mentioned in that light. (The bigger issue is that it's as yet undocumented!) – Christian Conkle Nov 23 '14 at 16:43
  • 1
    Language extensions like `Rank2Types`, `OverloadedStrings` and `TypeFamilies` are certainly helpful, but `NegativeLiterals` --- not so much. Anyway, that's only my view. – Aadit M Shah Nov 23 '14 at 16:50
  • @AaditMShah, `NegativeLiterals` is probably the cleanest way to deal with the nasty problem that `negate maxBound::Int > minBound`, which makes `-0x8000000000000000::Int` illegal Haskell 98. Also, while you're listing nice extensions, one of my very favorites is `ScopedTypeVariables`, which I think is good for newbies and advanced users alike. Also, I think the name `Rank2Types` is semi-deprecated in favor of `RankNTypes`, which actually does the same thing. – dfeuer Dec 01 '14 at 16:41
  • @dfeuer Except that `RankNTypes` makes type inference undecidable. – Aadit M Shah Dec 02 '14 at 02:49
  • @AaditMShah, `RankNTypes` and `Rank2Types` are actually the same extension. According to [the GHC user's guide](https://downloads.haskell.org/~ghc/7.8.3/docs/html/users_guide/other-type-extensions.html#universal-quantification), "The obselete[sic] language options `-XPolymorphicComponents` and `-XRank2Types` are synonyms for `-XRankNTypes`. They used to specify finer distinctions that GHC no longer makes." – dfeuer Dec 02 '14 at 04:31
2

You have not included the full error message in your question, and if you had, you'd see that repl 10 -1 is parsed as (repl 10) - (1) which is not what you intended.

You'd get the same error with repl 10 +1.

You can often find clues as to how a program code is parsed, by looking closely into the error message. There's no harm in overusing parentheses, while you learn, either.

The program:

repl x n = [x | _ <- [1..n]]        -- line 1

main = print (repl 10 -1)           -- line 3

The message:

prog.hs:3:8:
    No instance for (Show (t1 -> [t0])) arising from a use of `print'
    Possible fix: add an instance declaration for (Show (t1 -> [t0]))
    In the expression: print (repl 10 - 1)
    In an equation for `main': main = print (repl 10 - 1)

prog.hs:3:15:
    No instance for (Num t1) arising from a use of `repl'
    The type variable `t1' is ambiguous
    Possible fix: add a type signature that fixes these type variable(s)
    Note: there are several potential instances:
      instance Num Double -- Defined in `GHC.Float'
      instance Num Float -- Defined in `GHC.Float'
      instance Integral a => Num (GHC.Real.Ratio a)
        -- Defined in `GHC.Real'
      ...plus three others
    In the first argument of `(-)', namely `repl 10'         --------- NB!
    In the first argument of `print', namely `(repl 10 - 1)'
    In the expression: print (repl 10 - 1)

prog.hs:3:20:
    No instance for (Num t0) arising from the literal `10'
    The type variable `t0' is ambiguous
    Possible fix: add a type signature that fixes these type variable(s)
    Note: there are several potential instances:
      instance Num Double -- Defined in `GHC.Float'
      instance Num Float -- Defined in `GHC.Float'
      instance Integral a => Num (GHC.Real.Ratio a)
        -- Defined in `GHC.Real'
      ...plus three others
    In the first argument of `repl', namely `10'
    In the first argument of `(-)', namely `repl 10'         --------- NB!
    In the first argument of `print', namely `(repl 10 - 1)'

prog.hs:3:23:
    No instance for (Num (t1 -> [t0])) arising from a use of `-'
    Possible fix: add an instance declaration for (Num (t1 -> [t0]))
    In the first argument of `print', namely `(repl 10 - 1)'
    In the expression: print (repl 10 - 1)
    In an equation for `main': main = print (repl 10 - 1)
Will Ness
  • 70,110
  • 9
  • 98
  • 181
1

Did you try [1..(-1)]? In Haskell, you cannot write negative numbers like -1 directly. You need to put them in parentheses. The reason is that Haskell doesn't have prefix unary operators because operators in Haskell are always infix. Hence -1 is parsed as [operator (-)] [numeric 1] and not [numeric -1].

This is what causes the problem. To avoid this problem, negative numbers must always be put in parentheses. This ensures that (-1) is parsed as [numeric -1]. It's one of the few corner cases which gives migraines to newcomers in Haskell.

Rapptz
  • 20,807
  • 5
  • 72
  • 86
Aadit M Shah
  • 72,912
  • 30
  • 168
  • 299