Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Function composition and $ - one compiles, another doesn't

Tags:

haskell

Why does this compile well:

import Network.HTTP.Conduit (simpleHttp)
import qualified Data.ByteString.Lazy.Char8 as L

main = L.putStrLn . L.take 500 =<< simpleHttp "http://stackoverflow.com/"

but this doesn't:

main = L.putStrLn $ L.take 500 =<< simpleHttp "http://stackoverflow.com/"

For me these are exactly the same. The errors in the 2nd case are:

Couldn't match type `L.ByteString' with `m0 b0'
Expected type: L.ByteString -> m0 b0
  Actual type: L.ByteString -> L.ByteString
In the return type of a call of `L.take'
In the first argument of `(=<<)', namely `L.take 500'
In the second argument of `($)', namely
  `L.take 500 =<< simpleHttp "http://stackoverflow.com/"'

Couldn't match expected type `L.ByteString'
            with actual type `m0 b0'
In the second argument of `($)', namely
  `L.take 500 =<< simpleHttp "http://stackoverflow.com/"'
like image 563
Incerteza Avatar asked Dec 01 '22 01:12

Incerteza


2 Answers

(.) binds stronger than (=<<) and ($) binds weaker than (=<<). Therefore the first expression can be written as

main = (L.putStrLn . L.take 500) =<< simpleHttp "http://stackoverflow.com/"

and the second as

main = L.putStrLn $ (L.take 500 =<< simpleHttp "http://stackoverflow.com/")

So in the latter expression, the function =<<, which expects something like a -> m b as its first argument, is given L.take 500, which is a function from ByteString to ByteString. This doesn't fit together and thats what the error message says.

like image 52
Sven Koschnicke Avatar answered Dec 05 '22 04:12

Sven Koschnicke


(.) and ($) are never interchangeable, because their types are different:

(.) :: (b->c) -> (a->b) -> (a->c)
($) :: (b->c) ->   b    ->   c

It would have to hold that b ~ (a->b) and c ~ (a->c) whereas both are considered as "infinite type", causing an "occurs check" error, e.g. in

Prelude> let g a b = let x = a . b ; y = a $ b in undefined

<interactive>:1:24:
    Occurs check: cannot construct the infinite type: a = a1 -> a
    ....

Even more so in your example when the replacement of one with another causes an entirely different parse of the expression, because of their radically different precedences:

Prelude> :i (.)
(.) :: (b -> c) -> (a -> b) -> a -> c -- Defined in GHC.Base
infixr 9 .
Prelude> :i ($)
($) :: (a -> b) -> a -> b -- Defined in GHC.Base
infixr 0 $

edit: sometimes, in very specific circumstances, . and $ are interchangeable. Specifically, f $ g $ ... $ h $ x can be written as f . g . ... . h $ x.

This is not a contradiction to my opening statement, because

 f $ g $ x = f $ (g $ x) = ($) f (($) g x) 

whereas

 f . g $ x = (f . g) $ x = ($) ((.) f g) x

i.e. ($) was not replaced with (.); rather, the whole expression was rearranged.

like image 23
Will Ness Avatar answered Dec 05 '22 06:12

Will Ness