One of these days I was reading comments on the Monad Challenge (which I highly recommend to any beginner in Haskell, like myself), and I ended up on this thread where I read that ($) = id
.
I don't know about scaring people, but many programming languages have concepts that are best demonstrated with small examples that make people say "whoa".
For example, it's amazing that append() in Prolog can be run "backwards" from the concatenated result to yield all the lists that can be concatenated to produce it. Or that the monadic bind operator in Haskell (>>=) can be defined in terms of join and fmap, or that ($) = id.
($) = id !?
< tries it out in Raskell/Ghci >
I see now why it's true, but still... Whoah!! Thanks for that! (...)
I then checked the base
-4.10.0.0 code, looking for the definitions of ($)
and id
, but right at the top I read this:
NOTA BENE: Do NOT use ($) anywhere in this module! The type of ($) is slightly magical (it can return unlifted types), and it is wired in. But, it is also *defined* in this module, with a non-magical type. GHC gets terribly confused (and *hangs*) if you try to use ($) in this module, because it has different types in different scenarios. This is not a problem in general, because the type ($), being wired in, is not written out to the interface file, so importing files don't get confused. The problem is only if ($) is used here. So don't!
And their implementations are:
-- | Identity function.
id :: a -> a
id x = x
-- | Application operator.
{-# INLINE ($) #-}
($) :: (a -> b) -> a -> b
f $ x = f x
I tried swapping one by the other on GHCi and all I got was type errors (as I expected). Now, I have more questions than what I started with:
- What do they mean by saying that
($) = id
? - In which cases is that statement true? Does that mean I can use one instead of the other?
- In the
base
, what is meant by saying that($)
is "slightly magical (it can return unlifted types)" and "being wired in"? - And what about "different types in different scenarios"? I thought that since Haskell is a strongly typed language, once you define a type signature, that signature is retained until the end of Time. Is that not true? Are there cases where one can change a function's type?
($)
can be written as(a -> b) -> (a -> b)
(hello, currying). So basically it takes a function and returns the same function. Which isid
. – d12frosted