Related Issues?
I’m reading a few issues and dozens of proposals that seem intimately related:
#1263 - allow overloading of a.b field access
#5571 - function chaining
#16985 - custom infix operators
I think there is a challenge at the core of these issues is particularly important. Hence, I’m curious – what is the plan for addressing these issues? What is the current thinking? Will it be done for 1.0?
Direct Object Notation
Julia’s multiple-dispatch is fantastic. In the next decade, I think object-oriented single-dispatch will be seen as quaint and Julia will pave the road to the future. However, Julia risks throwing a baby out with the bath-water: direct object notation.
English : I love English
Javascript > I.love("Javascript")
Python > I.love("Python")
Ruby > I.love("Ruby")
...
Julia > love(I, "Julia")
I think the primary reason why object oriented languages are so prolific isn’t because of the dispatch object model. It’s because the syntax of these languages follow natural rules: they distinguish and prioritize a direct object over verbs and indirect objects. This syntax differentiation makes these languages easier to read, write, and reason about.
Critically, this notation supports processing pipelines or “fluent interface” library design though method-chaining. These sorts of data processing tools (LINQ, etc.) are central to Julia’s user community and the notational expression is important for usage.
Proposal Review
There are dozens of proposals, so pardon if I’ve missed a few. However, here are my thoughts on them. Let us assume I
is a computational object, and love
is a function with signature {type(I), String}
.
Method Notation
x.f() => f(x)
x.f(y) => f(x, y)
x.f(y, ...) => f(x, y, ...)
I.love("Julia")
This proposal would somehow permit .
to be overridden so that it could do a method lookup on f, rather than finding the matching attribute named f. This has the big advantage of being compatible with OO languages, at a price of being quite magical.
Curry Operator
x |> f(y) => f(y)(x)
I |> (x -> love(x, "Julia"))
This is the existing |> operator. It could only be used by libraries designed with currying in mind, otherwise it doesn’t solve the direct object notation problem. In particular, it can’t be used with contains(String, String).
1st Arg Pipeline
x |> f() => f(x)
x |> f(y) => f(x, y)
x |> f(y, ...) => f(x, y, ...)
I|>love("Julia")
This proposal would change the semantics of the |> to provide the 1st argument from the left-hand-side. It’s clean, but the syntax is a bit noisy. In particular, the > character is distracting which could be an issue for readability.
Placeholder Pipeline
x |> f(_, y) => f(x, y)
This proposal uses _ as a place holder. While it’s a very general syntax, but even more noisy than the previous.
I|>love(_, "Julia")
Nth Arg Pipeline
x |> f(y) => f(y, x)
This mechanism seems unexpected to me. Does it help with chaining? It also doesn’t work with common built-ins, like contains(String, String).
"Julia" |> love(I)
Plain Infix Notation
x f y => f(x, y)
This is a infix only notation which could be chained, but, wouldn’t apply to unary or ternary functions. It’d help, but it wouldn’t address core problem.
I love "Julia"
Parenthesized Infix
(x f y) => f(x, y)
This notation would admit unary and n-ary functions, however, it isn’t chainable. Being chainable is quite important to the core challenge.
(I love "Julia")
Infix Macro
x @f y => @f(x,y)
This path permits infix definition of macros. While this may be better than the previous (it handles n-ary and chains) it means most libraries will be heavy on macros.
I @love "Julia"
Reclaim Bitwise Indicators?
This is the last possible time Julia might consider reclaiming bitwise indicators (especially |
character) to address this problem and future language evolution. While bitwise operators are traditional, they are, for the most part, quite uncommon in modern computing – they could easily be given two-character sequence. I suggest that Julia designers may ask if it’s worth reclaiming the pipe (|) indicator, and perhaps reserve more of the single-character bitwise operators for future language evolution. In particular, the 1st Arg proposal could be dramatically improved if the pipline character (|
) traditional in shell could be used:
x | f() => f(x)
x | f(y) => f(x, y)
x | f(...) => f(x, ...)
I | love("Julia")