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:
Num (a -> b)
syntax mean?($)
in this way, that starts with something like ($) (2)
?Thanks!
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 anInteger
.
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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With