Fixing the Piping/Chaining/Partial Application Issue (Rev 2)


A Fun (and productive) Experiment

A repeated complaint about “tight-currying” is that it binds too tightly for a lot of the common expressions we might wish for. For example, how do we express 1 + x - 2?

julia> demo" 1 + _ - 2 "
ERROR: MethodError: no method matching -(::Fix1{typeof(+), Int64}, ::Int64)

Oh no! An error! That’s because “tight-currying” was greedy, and now we’re trying to subtract an integer from a function instead of just having a lambda that represents the entire expression.

But remember the point of simple syntax with consistent rules, is that it’s composable with other syntax and operators. So maybe we can solve the problem with… wait for it… composition! (ba-dum-tsss)

julia> Base.:-(x::Fix, y) = Fix2(-, y) ∘ x

julia> demo" 1 + _ - 2 "
-(_, 2) ∘ +(1, _)

julia> demo" 1 + _ - 2 "(3)
2

Yay!

Here’s another example: \sin(\cos(x)):

julia> Base.sin(x::Fix) = sin ∘ x

julia> demo" sin(cos(_)) "
sin ∘ cos(_)

julia> demo" sin(cos(_)) "(1)
0.5143952585235492

Composing tight currying with function composition. A thing of beauty.

Now, because any object can be a function, maybe this behavior shouldn’t be constrained to just Fix objects…

BatmanThinkingGIF

If nothing else though, the type Fix makes it explicit that it’s a partial function rather than any arbitrary function or object, and therefore this behavior could be intended, so it seems safe enough to explore.