4

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/"'
Incerteza
  • 32,326
  • 47
  • 154
  • 261
  • possible duplicate of [Haskell: difference between . (dot) and $ (dollar sign)](http://stackoverflow.com/questions/940382/haskell-difference-between-dot-and-dollar-sign) – sigrlami Jul 16 '14 at 16:41

2 Answers2

17

(.) 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.

Sven Koschnicke
  • 6,523
  • 2
  • 34
  • 49
  • 1
    You sort of missed the punchline -- the `L.take` expression isn't a `Bytestring`, and that's why it doesn't compile, which is what the error message says. – Benjamin Kovach Jul 15 '14 at 12:28
  • 3
    I added a explaination, thanks @BenjaminKovach! I think my explaination is nearer to the error message than just stating that the expression isn't a `ByteString`. What do you think? – Sven Koschnicke Jul 15 '14 at 12:43
3

(.) 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.

Community
  • 1
  • 1
Will Ness
  • 70,110
  • 9
  • 98
  • 181
  • by the way, I can't find the precedence of the casting operator "::". What's its precedence? – Incerteza Jul 16 '14 at 23:07
  • 1
    @AlexanderSupertramp `::` isn't properly an operator, it's a piece of syntax. If it were an operator you'd be able to say `let weird = (::) in weird value type` instead of `value :: type`; you can't. Anywhere you can have a value expression you can also have `value :: type`, and it attaches "as far out" as it can without producing a syntax error. So you can think of it as being "even lower precedence" than `$`, but it's not really the same thing as normal operator precedence that causes it to do this. – Ben Jul 18 '14 at 06:47
  • 1
    @AlexanderSupertramp (Also "casting" isn't what it does; `foo :: bar` is just a declaration that `foo` has type `bar`; this declaration can be either true or false, but it doesn't *change* the type of `foo`) – Ben Jul 18 '14 at 06:48
  • @Ben, of course it doesn't change its type. So if there "::" at the end of an expression then it'll be applied when the expression is evaluated? – Incerteza Jul 18 '14 at 06:58
  • @AlexanderSupertramp Well, it's completely stripped away during compilation; it doesn't even exist when the expression is being evaluated at runtime. But I'm not sure that's what you meant by "when the expression is evaluated"? It's just extra information the compiler can use to decide whether the program is well-typed or not. – Ben Jul 18 '14 at 07:10