Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell partial function application with $

I'm new to Haskell and looking at a simple example of using function application with $.

It seems straightforward - it takes a function and applies it to a value.

So this makes sense:

> (+3) $ 2
5

This also makes sense:

> ($) (+3) 2
5

This makes sense because the first argument is the function, and the second argument is the value.

Now considering using $ to create a partial function.

Looking at types, this makes sense - it just needs a Num type value for b:

> :t ($) (+3)
($) (+3) :: Num b => b -> b

But here's where I get lost - what is happening here?:

> :t ($) (2)
($) (2) :: Num (a -> b) => a -> b

I would have expected the first argument would need to be a function, not a simple Num value.

So here's my questions:

  1. What's happening here?
  2. What does the constraint Num (a -> b) syntax mean?
  3. What's an example of using ($) in this way, that starts with something like ($) (2)?

Thanks!

like image 489
Kevan Stannard Avatar asked Jan 01 '23 08:01

Kevan Stannard


1 Answers

On the one hand, numeric literals like 2 are actually read as fromInteger 2 :: Num a => a so can denote any value of type Num a => a, meaning, any type which is in type class Num, i.e. has among other things a special version of fromInteger defined which returns the actual value of the actual type, converted from the integer 2:

> :i Num
class Num a where
  (+) :: a -> a -> a
  (*) :: a -> a -> a
  (-) :: a -> a -> a
  negate :: a -> a
  abs :: a -> a
  signum :: a -> a
  fromInteger :: Integer -> a

As the Haskell Tutorial puts it (in 10.3),

An integer numeral (without a decimal point) is actually equivalent to an application of fromInteger to the value of the numeral as an Integer.

On the other hand, ($) has the type

> :t ($)
($) :: (a -> b) -> a -> b

So we have

fromInteger 2 :: Num a1 =>   a1
($)           ::          (a -> b) -> a -> b
--------------------------------------------
($) 2         :: Num      (a -> b) => a -> b

So it's a function, which must also be in the type class Num.

Normally that's not the case but Haskell doesn't know if you could be importing some module which does define such an instance:

instance Num (a -> b) where
   ....
   fromInteger n = ....
   ....

so it allows this possibility at the type checking, and only then, seeing that there is no such actual instance defined anywhere, it errors out on that.

For example, following the hint from @augustss in the comments,

instance (Num b) => Num (a -> b) where
   (+) f g x = f x + g x
   (*) f g x = f x * g x
   abs f x = abs (f x)
   negate f x = negate (f x)
   signum f x = signum (f x)
   fromInteger n = const (fromInteger n)

lets us write (sin + 2 * cos^2) x.

like image 85
Will Ness Avatar answered Jan 07 '23 23:01

Will Ness