What actually $ function does in haskell? [duplicate]
Asked Answered
L

3

9

I know

$ :: (a->b) -> a -> b
f $ x = f x

Intuitively it seems to me, like to say, 1. $ delays the evaluation of the function to its left 2. evaluates whats to its right 3. feeds the result of its left to its right.

And it makes perfect sense to me when,

ghci> length $ [1..5]
5
ghci> ($) length [1..5]
5

What I do not understand is why,

ghci> ($ [1..5]) length
5

Judging from the type of $, isn't that its (first) argument should be a function ?

Lofty answered 3/4, 2013 at 10:25 Comment(8)
This isn't about ($), but about operator sections.Inwardness
$ doesn't delay evaluation of the function to it's left. You may be confusing it with $!, which forces partial evaluation of the argument to its right before feeding it to the function to its left.Hendricks
This is "section" syntax at work. haskell.org/onlinereport/haskell2010/…Levalloisian
And as to what this is good for: you can use it when you want to apply a list of functions to one value: map ($ [1..5]) [length,sum,product].Inwardness
@Hendricks id $! x == x `seq` id x doesn't force anything, or so I've been told.Randee
@WillNess Well, seq, or ($!) are only evaluated when their result is demanded. Since argument and result of id are the same, id $! x forces the evaluation of the argument, x, (to weak head normal form) exactly when the result, x, needs to be evaluated (to WHNF). So it's rather hard to determine whether the ($!) did anything there. But in an implementation where a `seq` b evaluates a before b (that is not required by the semantics of seq), if you look hard enough, you can see that id $! undefined errors before id returns. </pointless pedanticism>Messere
@DanielFischer the reason I dwell on this is that I was confused by this quite a few times by assuming that $! "forces its argument". The real question, when?, is left out by this magical statement. Would it be better to say that $! "arranges for its argument to be forced before being fed to the function, when the result of function application is forced"?Randee
@Will Common confusion. I have of course never suffered from it (whistles innocently). The answer is, a function cannot do anything - in particular not force the evaluation of one of its arguments - before it is called. When is it called? When its result needs to be evaluated, no earlier, no later. Regarding your edit, yes, it would probably be better to say ($!) forces its second argument when its result is needed to be evaluated.Messere
E
16

This has to do with parsing. In Haskell you can write (op arg) where op is an infix operator. This is not the same as ((op) arg). And you can write (arg op) as well! For example:

GHCi, version 7.0.3: http://www.haskell.org/ghc/  :? for help
Prelude> :t (+ 4)
(+ 4) :: Num a => a -> a
Prelude> :t (4 +)
(4 +) :: Num a => a -> a

That is, (+ 4) is the function \x -> x + 4 and (4 +) is the function \y -> 4 + y. In the case of addition these are equal functions, but that is not really important right now.

Now let us try the same trick on $:

Prelude> :t ($ [1,2,3,4])
($ [1,2,3,4]) :: Num t => ([t] -> b) -> b

Now surprise so far, we got \f -> f $ [1,2,3,4]. We can also write

Prelude> :t (length $)
(length $) :: [a] -> Int

to get the function \l -> length $ l. But how about this:

Prelude> :t ($ length)
($ length) :: (([a] -> Int) -> b) -> b

This is strange, but it makes sense! We got \f -> f $ length, i.e., a functional which expects to get a function f of type ([a] -> Int) -> b) that will be applied to length. There is a fourth possibility:

Prelude> :t ([1,2,3,4] $)

<interactive>:1:2:
    Couldn't match expected type `a0 -> b0' with actual type `[t0]'
    In the first argument of `($)', namely `[1, 2, 3, 4]'
    In the expression: ([1, 2, 3, 4] $)

Everything is as it should be because [1,2,3,4] is not a function. What if we write $ in parenthesis? Then its special meaning as an infix operator disappears:

Prelude> :t (($) length)
(($) length) :: [a] -> Int

Prelude> :t (($) [1,2,3,4])
<interactive>:1:6:
    Couldn't match expected type `a0 -> b0' with actual type `[t0]'
    In the first argument of `($)', namely `[1, 2, 3, 4]'
    In the expression: (($) [1, 2, 3, 4])

Prelude> :t (length ($))
<interactive>:1:9:
    Couldn't match expected type `[a0]'
                with actual type `(a1 -> b0) -> a1 -> b0'
    In the first argument of `length', namely `($)'
    In the expression: (length ($))

Prelude> :t ([1,2,3,4] ($))
<interactive>:1:2:
    The function `[1, 2, 3, 4]' is applied to one argument,
    but its type `[t0]' has none
    In the expression: ([1, 2, 3, 4] ($))

So, to answer your question: $ [1,2,3,4] is parsed as \f -> f $ [1,2,3,4] so it makes perfect sense to apply it to length. However ($) [1, 2, 3, 4] does not make much sense because ($) is not seen as an infix operator.

By the way, $ does "not do anything", so to speak. It is mostly used for more readable input because it has low precedence and so we can write f $ g $ h $ x instead of f (g (h x)).

Eagre answered 3/4, 2013 at 10:42 Comment(7)
- may have been a bad choice of example, as it's the one and only exception where (op arg) does not represent a function.Omari
Also, both ((-) 4) and (4 -) are the same function, (\x -> 4 - x).Omari
Does it mean because (-) is left associated, both ((-) 4) and (4 -) means (\x -> 4 - x), while ($) is right associated, both ($ func) and ($ arg) means (\x -> bar $ a) where a means func or arg ?Lofty
Oops, let me fix that. Thanks, Scott.Eagre
This has nothing to do with associativity. The thing which is confusing @Lofty is that f $ length makes sense if f has the correct type. Thus Haskell is parsing $ length as \f -> f $ length, just like $ [1,2,3,4] is parsed as \f -> f $ [1,2,3,4].Eagre
OK, I see. ($ length) ((*2).) [1..10] does give me an expected 20. However, it is still very confusing that ((-) 4) is parsed as (\x -> 4 - x ) but not (\x -> x - 4)Lofty
It's because ((-) 4) isn't an operator section at all. It's just normal partial application of -, and it is given its arguments left-or-right like every other function. Since it takes its left argument first, you get (\x -> 4 - x).Omari
O
10

Your question is really about what is called operator sections. With any operator in Haskell (I will use + as an example) you can write something like (+ arg) or (arg +). These are just shorthand syntax for the anonymous functions (\x -> x + arg) and (\x -> arg + x), respectively.

So, the ($ [1..5]) syntax just means (\x -> x $ [1..5]) which is the same as (\x -> x [1..5]) (ie. a function which passes [1..5] to the function passed as its argument).

Omari answered 3/4, 2013 at 10:30 Comment(1)
This (\x -> x $ [1..5]) really clears up my confusion!Lofty
S
6

($ [1..5]) is a section. That's a partially applied operator. It's a shorthand for (\f -> f $ [1..5]).

Sections let you supply one argument to a binary operator and produce a function - a function which is waiting for the remaining argument.

Take a look at http://www.haskell.org/haskellwiki/Section_of_an_infix_operator

Salcedo answered 3/4, 2013 at 10:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.