Partial Application with Infix Functions
Asked Answered
A

3

13

While I understand a little about currying in the mathematical sense, partially applying an infix function was a new concept which I discovered after diving into the book Learn You a Haskell for Great Good.

Given this function:

applyTwice :: (a -> a) -> a -> a
applyTwice f x = f (f x)

The author uses it in a interesting way:

ghci> applyTwice (++ [0]) [1]  
[1,0,0]
ghci> applyTwice ([0] ++) [1]
[0,0,1]

Here I can see clearly that the resulting function had different parameters passed, which would not happen by normal means considering it is a curried function (would it?). So, is there any special treatment on infix sectioning by Haskell? Is it generic to all infix functions?


As a side note, this is my first week with Haskell and functional programming, and I'm still reading the book.

Almetaalmighty answered 12/4, 2012 at 20:28 Comment(0)
S
19

Yes, you can partially apply an infix operator by specifying either its left or right operand, just leaving the other one blank (exactly in the two examples you wrote).

So, ([0] ++) is the same as (++) [0] or \x -> [0] ++ x (remember you can turn an infix operator into a standard function by means of parenthesis), while (++ [0]) equals to \x -> x ++ [0].

It is useful to know also the usage of backticks, ( `` ), that enable you to turn any standard function with two arguments in an infix operator:

Prelude> elem 2 [1,2,3]
True
Prelude> 2 `elem` [1,2,3] -- this is the same as before
True
Prelude> let f = (`elem` [1,2,3]) -- partial application, second operand
Prelude> f 1
True
Prelude> f 4
False
Prelude> let g = (1 `elem`) -- partial application, first operand
Prelude> g [1,2]
True
Prelude> g [2,3]
False
Samuelson answered 12/4, 2012 at 20:37 Comment(2)
So, I don't know Haskell, but is (1 `elem`) the same as elem 1?Toluol
I like to think of (++) as a section where you omit both inputs.Osmometer
T
16

Yes, this is the section syntax at work.

Sections are written as ( op e ) or ( e op ), where op is a binary operator and e is an expression. Sections are a convenient syntax for partial application of binary operators.

The following identities hold:

(op e)  =   \ x -> x op e
(e op)  =   \ x -> e op x
Tallu answered 12/4, 2012 at 20:38 Comment(0)
M
5

All infix operators can be used in sections in Haskell - except for - due to strangeness with unary negation. This even includes non-infix functions converted to infix by use of backticks. You can even think of the formulation for making operators into normal functions as a double-sided section:

(x + y) -> (+ y) -> (+)

Sections are (mostly, with some rare corner cases) treated as simple lambdas. (/ 2) is the same as:

\x -> (x / 2)

and (2 /) is the same as \x -> (2 / x), for an example with a non-commutative operator.

There's nothing deeply interesting theoretically going on here. It's just syntactic sugar for partial application of infix operators. It makes code a little bit prettier, often. (There are counterexamples, of course.)

Muniz answered 12/4, 2012 at 20:38 Comment(3)
Note that the "strangeness with unary negation" is due to the fact that it is ambiguous whether (- 1) should be interpreted as the numeric literal -1 or as the function \x -> x - 1. The choice of the creators of Haskell was to interpret it as a numeric literal, and provide the function subtract, which satisfies subtract x = \y -> y - x. They also provide negate which acts as the unary minus function, i.e. negate x = -xLogistician
@ChrisTaylor: Almost. (- x) translates to negate x, so (- 1) is negate (fromInteger 1), not fromInteger (-1). Of course, the two should be equivalent for well-behaved Num instances.Dictaphone
@Dictaphone Ah ok, little bit of backwards logic there. Thanks for the correction!Logistician

© 2022 - 2024 — McMap. All rights reserved.