0

In Haskell, I can give too few arguments to a function to get back a curried function:

-- the addition function
Prelude> :t (+) 
(+) :: Num a => a -> a -> a

-- curried with its first argument => the increment function
Prelude> :t (+) 1
(+) 1 :: Num a => a -> a

-- supplied with both arguments => the result
Prelude> :t (+) 1 2
(+) 1 2 :: Num a => a

But what do I get when I supply too many arguments?

Prelude> :t (+) 1 2 3
(+) 1 2 3 :: (Num a, Num (a -> t)) => t

What is this, does it have a name, and is it useful for anything?

Thilo
  • 257,207
  • 101
  • 511
  • 656
  • 2
    http://stackoverflow.com/questions/40592338/how-to-read-the-following-haskell-definition-num-a-b-a-b – leftaroundabout Nov 17 '16 at 14:07
  • Possible duplicate of [How to read the following haskell definition " Num (a -> b) => \[a\] -> \[b\] "](http://stackoverflow.com/questions/40592338/how-to-read-the-following-haskell-definition-num-a-b-a-b) – Babra Cunningham Jan 12 '17 at 10:53

2 Answers2

9

It's useful to think about it this way. You always give exactly one argument to a function. f a b is exactly equivalent to (f a) b.

So (+) 1 2 3 is the same as

 (((+) 1) 2) 3

Now we know what ((+) 1) 2 is, it's the same as (+) 1 2 or 1 + 2 which is just 3. So the expression boils down to 3 3.

How come it's not an error?

Integer literals in Haskell are overloaded. 3 could be of any type, provided this type has a Num instance. There is nothing illegal to give a Num instance to a function type either. The type inferencer tells you just that:

(Num a, Num (a -> t)) => t

This can be read as

For any type a which has a Num instance, and any type a->t which also has a Num instance, the expression in question has type t.

Of course in practice such instances are unlikely, but one can in principle define them, and make the expression (+) 1 2 3 evaluate to a well-defined value.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
  • I see. So someone could theoretically make an instance of `Num (a -> t)` for `Num a`, and then you'd want `(+) x y z` to check out for values of this type. – Thilo Nov 17 '16 at 14:30
  • Could that theoretically be made to work even for literals? Is there a way to augment the interpretation of numeric literals in such a way that `1` can become a function value (or anything else other than the conversions your compiler has built-in)? – Thilo Nov 17 '16 at 14:31
  • 2
    Yes, having an instance of Num implies that you can use integer literals for your type. This is handled by `fromInteger` method. So `3` is really a `fromInteger (3::Integer)`. You get to write `fromInteger` for your instance so you can have 3 mean whatever (for examlple `const 3`). – n. m. could be an AI Nov 17 '16 at 15:06
5

In general, you just get an error, because the result of applying a function to "enough" arguments does not produce a value that can be applied to another value as a function.

> (++) [1] [2] [3]

<interactive>:1:1: error:
    • Couldn't match expected type ‘[Integer] -> t’
                  with actual type ‘[Integer]’
    • The function ‘(++)’ is applied to three arguments,
      but its type ‘[Integer] -> [Integer] -> [Integer]’ has only two
      In the expression: (++) [1] [2] [3]
      In an equation for ‘it’: it = (++) [1] [2] [3]
    • Relevant bindings include it :: t (bound at <interactive>:1:1)

In the case of (+), though, the return value can be of any type that has an instance of Num. Since (+) 1 2 has type Num a => a, then if a -> t also had an instance of Num, it would be fine to assume that (+) 1 2 could be applied again to a third value. This is a demonstration that type classes are open.

chepner
  • 497,756
  • 71
  • 530
  • 681
  • So I ran into this because the numeric literals are still "flexible" and could be interpreted as different instances of `Num`, and as far as the type-checker is concerned `1` could end up not just as an `Int` or `Fractional`, but also as an `a -> t` ? – Thilo Nov 17 '16 at 14:21
  • I tried to force the arguments to be `Int` rather than `Num a`, and then it did immediately produce an error: `let one = 1 :: Int :t (+) one one one --> type error` – Thilo Nov 17 '16 at 14:24