I think the dots are a good proposal; it's a fairly light syntax and
the fortran precedent makes it not-totally-random.
The "operator_" prefix is interesting. It never would have occurred to
me, as I'm generally dead-set against any magic renaming or having
"real" names different from apparent names.
It's neat that you can do
"a .x. b", but I don't think it's worth it.
In that case, how about using the same notation currently being used for range operator? That is, you'd write 2:^:3 if ^ weren't already an infix function. Similarly, you might write 17:mod:5 to evaluate mod(17,5). Right now, if you try either of these (in 0.2.1), it responds with "ERROR: no method colon(Int64,Function,Int64)".
As a result, even now, you can actually use this notation to manually create infix operators by simply adding this to ~/julia-rc.jl:
colon(x,y::Function,z)=y(x,z)
After this, 17:mod:5 produces 2, 2:^:3 produces 8, and "Hello ":print:"world!" prints Hello world!
The only binary functions I know of that you can put into that notation and NOT get the desired result are + and -, which still trigger the normal range operator (1:+:3 = 1:3, 3:-:1 = 3:-1:1). I'd suggest removing this curious behaviour of a:+:b and a:-:b (given that the former is the same as a:1:b and the latter is a:-1:b, anyway), and then building the aforementioned colon functionality into the language directly.
If I may offer an alternative... there's already a way to create infix functions using an existing character that shouldn't create any particular ambiguities*, simply through adding a line to (in Ubuntu, at least) ~/.juliarc.jl - in particular, you can put the line
colon(x,y::Function,z)=y(x,z)
With this line, you can write, for instance, 2:^:3 (=8), 17:mod:5 (=2), or "Hello ":print:"world!" (which prints Hello world!). I believe it will work with any binary function except + or -, which seem to be handled separately (1:+:3 works the same as 1:3, and 3:-:1 works the same as 3:-1:1). It even works with anonymous functions - "7:((x,y)->x^2+y^2):9" outputs 130, which is indeed 7^2+9^2.
If this could be incorporated into the base code, it would address the issue cleanly and efficiently, in my opinion.
* The only possible ambiguity is with the regular ternary operator... that is, how would it parse true?"Hi":print:" there":1 - as it turns out, with the definition provided above, it outputs the text value "Hi" rather than printing "Hi there". Note that the same ambiguity already exists - true?1:2:3:5 will result in 1, not 1:2:3, while false?1:2:3:5 will result in 2:3:5.
On Wednesday, 12 February 2014 05:13:33 UTC+10, Steven G. Johnson wrote:
If I may offer an alternative... there's already a way to create infix functions using an existing character that shouldn't create any particular ambiguities*, simply through adding a line to (in Ubuntu, at least) ~/.juliarc.jl - in particular, you can put the line
colon(x,y::Function,z)=y(x,z)
a :b: c :d: e
do? I know this is unambiguous for dot products but
b(a, d(c, e))
is always obvious.
I really think this needs some thoughts : to me, it seems just like "hey guys, why not doing this just because we can and it seems shinny". I would love to see what problem such suggar would help to solve. And if it's really helpful, then why not.
However, I'm definitly against creating infix functions without any distinctive syntax features. The column trick seems great in that regard.
But again, I'm not sure adding complexity and redundancy to the language is a good idea : c++ has done this for years and now it's a real mess. If someone only learns c++11 and wants to contribute to any project, he will have a really bad time trying to catch up with all the alternative "syntax" people are using. I'm not saying infix functions should not exist, only that it should be a part of "Julia's way of solving problem", not a shinny stuff that make programs harder to understand. If it can't then it might not be a good idea after all.
I know that most of you focus on ease of use, but we are in the 21st century. Let's stop doing approximative science on our own. Let's share our progress and more importantly, built upon what already exists instead redoing everything from scratch each time.
This can't be done with ugly source code for which the incentive to rebuild everything from scratch is very high. That's why code readability should be considered just as important as ease of use.
Fortran is simple but some of its design choices made it unreadable.
Even personal, quickly writen python scripts are readable which make their translation to a proper lib a painless operation. If Julia can't provide that, it's gonna follow Fortran trend : successful but painful to reuse. There will be some very high quality libs available and aside from that, spaghetti.
The usual standard is that operators at equal precedence run left-to-right. So a :b: c :d: e would be (a :b: c) :d: e, and thus d(b(a,c),e) - just as 25/5/5 evaluates to 1.0, not 25.0. And in the majority of situations, this is the behaviour that users would expect. Interestingly, the colon operator is lower precedence than addition. What this means is that 4+7 :*: 3+9 would actually be same as 11*12. I just mention this as an aside.
The idea behind having an infix operator is that, in many situations, the roles of the two inputs to the function are asymmetric, in the sense that one is the input being operated on, and the other is part of the operator itself. For instance, modulo. mod(13,7) is less clear than 13 mod 7.
A bit of redundant coding choice is good for code readability - it means that the programmer can, if they're paying attention, choose the best way to write their code in order to make it as readable as possible. If the programmer is thinking about it, they probably aren't going to write x :div: 13 :mod: 11, but rather, (x :div: 13) :mod: 11. And they'll write print("Hello ", "world!") rather than "Hello ":print:"world!", because the former is clearer than the latter. Same as how we use 3+4 rather than +(3,4), because 3+4 is much clearer. In a similar sense, I often construct ranges by writing 1:(x+1), even though 1:x+1 evaluates the same... because it avoids the ambiguity with (1:x)+1. But having the choice to use 1:x+1 means that, in some cases, clearer code can be written.
Of course, it's important to ensure that it's not 100 different ways to write the same thing. Basically, it's a matter of balance. And I'd suggest that infix functions improve the balance.
I understand for :mod: but isn't it more an exception than the rule? That's why many languages use % as the modulo operator. Having a few infix functions does not mean that user defined infix operators should be allowed. I'm not well versed in mathematics but to my knowledge, only very few asymetric operations are widely used. All I'm saying is that Julia would become more complicated with that and I'm wondering if the extra complexity is worth it.
Regarding code clarity, it's pretty easy to craft a very clear, understandable and maintainable library in Fortran. However in general, code base in that language are ugly.
It is not about letting users writing clear code, it's about letting non computer scientist, time-constrained users writing a code as clear as possible from the start. Allowing clear constructs is not enough, those clear constructs should be considered the default, fast, obvious Julia way's to do things. In that regard adding redundancy might be a very good thing (your modulus example is very good), but is it worth it as a new language feature?
In that regard adding redundancy might be a very good thing (your modulus example is very good), but is it worth it as a new language feature?
Pvnpo
I like the readability of |>op<|, but I don't like the tricks like<|(a,b) = i->a(i,b)Finally, it allows defining functions in their infix form directly:a::Integer
I like the readability of |>op<|, but I don't like the tricks like
<|(a,b) = i->a(i,b)
because doing it in the parser directly avoids function overhead, makes combining with macros for DSLs easier, and allows defining functions in their infix form directly:
The proposals for a delimiter character so far are:
[...]
Cons:-hard to type
-Some people find `%` ugly.
One question, why '>% whatever %<', instead of '<% whatever %>' or '%< whatever >%' ?Personally, I think having the the angles < > is much more readable, because we are used to using them in pairs, like (), [], {}.
In my ideal world, \operator would allow for custom operators (like in LaTeX),
and you could specify precedence like in Haskell.
And function chaining would use | (like Unix shell).
In my ideal world, \operator would allow for custom operators (like in LaTeX),
Right, there are much more important things going on to make it to Julia 1.0.
A nice thing I think about Jameson's idea (whether `<%op%>`, `%<op>%` or `>%op%<`) is that it could be added post-1.0, as it doesn't introduce any incompatibilities.
To answer your questions......
You mention the possible <<| and |>> operators to provide modified orders. I prefer maintaining a standard, such as >| for inserting a variable at the start and |< for evaluating at the end. That way, pipe first means evaluation, and pipe last maintains it as a function, while the > and < indicate which one is the function.
Concerns with your proposal are:- Confusion of notation, with % being used for multiple meanings, which might conflict. If you want to perform the remainder operation twice, (a%b)%c could easily be written as a%b%c - and that is easily confused with a %b% c - this is precisely why my original suggestion of a:b:c was rejected, because what it does depends on what b is, and they're very different depending on that choice.
- Major parser alterations, because the %op% notation doesn't exist at this time, whereas my proposal can be implemented functionally with a single line of code and two small edits to the parser's precedence list (namely, editing the line in julia-parser.scm that says "(define prec-pipe '(|\|>| |<\||))" to be two lines, with "(define prec-pipe '(|\|>|))" followed on the next line by "(define prec-pipe-left '(|<\||))", then editing the "(define prec-names" section to add "prec-pipe-left" after "prec-pipe". This fixes the fact that |> and <| are currently equal precedence, and thus evaluate left-to-right, whereas we need it to go the other way.
- If you have A=[sum,prod,maximum], you'll run into some issues with the idea of %A[1]%, as it's not clear how it should be read. This could be a real issue if someone wants to define % for their variable types, and can take a function as an argument.
- Needing whitespace to distinguish between uses of % could lead to some difficult to find bugs in code.
On the other hand, my suggestion (at least, the infix part of it) could be done by a macro,
Regarding the multiple % usage, it's certainly not going to be common, but you really don't even want a corner case to be vague when it comes to a parser. Beyond that, suppose you have a%b %c% d. Suddenly, it's not obvious at a glance what is going on.
The operator <| already exists... it just isn't defined. It's set up so that it *can* be used as an operator, but if you try to use it without defining it, it will throw an error. The concern isn't existence, it's precedence. The logical option, when setting up the |> operator, was to make <| have the same precedence. But with the current precedence, a|>b<|c gets interpreted as (a|>b)<|c, which means it become i->b(a)(i,c), which is clearly not the intended interpretation.
Regarding the "I put the pipe on the wrong side of the arrow", I don't see that as likely. |> is hard to confuse with >|, and similarly, <| is hard to confuse with |< - they look completely different, the difference between a triangle and a K. It's much different from the comparison between a%b %c% d and a %b% c%d. Whitespace isn't a good way to distinguish between two uses of an operator, when those uses are dramatically different but could be applied to the same objects.
One example of where someone might define % used with a function is functional modulo. For instance, (x->x^3-3x^2+2x-1)%(x->x^2-1) would evaluate to 2x-3.
On Wednesday, 15 June 2016 13:52:29 UTC-4, Glen O wrote:Regarding the multiple % usage, it's certainly not going to be common, but you really don't even want a corner case to be vague when it comes to a parser. Beyond that, suppose you have a%b %c% d. Suddenly, it's not obvious at a glance what is going on.I think that if %op% is used, then the % operator without whitespace on either side should be deprecated.
I think that if %op% is used, then the % operator without whitespace on either side should be deprecated.That might break a lot of code. If you are doing bit twiddling work in Julia (which I do!), you end up using the idiom `value%type`, typically with Type being UInt8/16/32 or Int8/16/32, which kind of "casts" the value to the given type, but without the `InexactError` which can occur using the constructor (i.e `Int8(1000)`).
Your main concern regarding |> <| seems to be the "functional overhead".