tags:

views:

181

answers:

2

After playing around with haskell a bit I stumbled over this function:

Prelude Data.Maclaurin> :t ((+) . ($) . (+))
((+) . ($) . (+)) :: (Num a) => a -> (a -> a) -> a -> a

(Data.Maclaurin is exported by the package vector-space.) So it takes a Num, a function, another Num and ultimately returns a Num. What magic makes the following work?

Prelude Data.Maclaurin> ((+) . ($) . (+)) 1 2 3
6

2 is obviously not a function (a->a) or did I miss out on something?

+14  A: 

The Data.NumInstances module of the same package defines a Num instance for functions that return numbers:

instance Num b => Num (a->b) where
  (+)         = liftA2 (+)
  (*)         = liftA2 (*)
  fromInteger = pure . fromInteger
  ...

In Haskell an integer literal like 2 is generic so that it can represent a number for any instance of Num:

Prelude> :t 2
2 :: (Num t) => t

To convert it to an actual number of the type required in a specific context, fromInteger from the Num class is called.

Since the helper module mentioned above defines an instance of Num for functions, 2 can now be converted to a function with the fromInteger method specified there. So ghci calls fromInteger 2 to get the function required as the second parameter of the construct in the question. The whole expression then happens to evaluate to 6.

sth
+1  A: 

You have good reason to be confused. Using the Data.NumInstances module in GHC (which is loaded by Data.Maclaurin) it is possible to coerce a Num to a constant function.

Prelude Data.NumInstances> :t (2 :: (Num a) => a -> a)
(2 :: (Num a) => a -> a) :: (Num a) => a -> a
Prelude Data.NumInstances> (2 :: (Num a) => a -> a) 0          
2
Prelude Data.NumInstances> (2 :: (Num a) => a -> a) 1000
2

The evaluation of the expression is, essentially,

((+) . ($) . (+)) 1 2 3 = ((+) . ($) . (1+)) 2 3
                        = ((+) (1+)) 2 3
                        -- (+) is defined for functions that return a Num
                        = ((+) (1+) (\_ -> 2)) 3  
                        = ((+2) . (1+)) 3
                        = 6
Chris Conway
No it's not possible, unless you have some nonstandard modules loaded which define a `Num` instance for `a -> a`.
Reid Barton
Well, damn, I could swear this worked without Data.Maclaurin loaded, but it doesn't. As @sth points out, this magic is enabled by Data.NumInstances.
Chris Conway