Partial Application with Infix Functions

2019-04-19 21:04发布

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.

3条回答
聊天终结者
2楼-- · 2019-04-19 21:11

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
查看更多
Animai°情兽
3楼-- · 2019-04-19 21:18

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

查看更多
我命由我不由天
4楼-- · 2019-04-19 21:25

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
查看更多
登录 后发表回答