-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal: Custom Operators / Infix Functions #427
Comments
This is a really coherent idea, thank you for the proposal. I can think of 2 reasons that make me hesitant to go down this road, despite the (correct) observation that the UFCS way looks like it's mutating the vecs.
Milestone 0.2.0 means we'll accept and implement or reject this proposal before 0.2.0 release. |
A trivial counter to the point about mutating the parameters is an application level solution: just name the functions differently so they sound more like pure functions.
This has nothing to do with infix operator/function syntax; mutable |
This proposal by @raulgrell could serve as an alternative to supporting UFCS. In order to see how this could be useful, consider a common use case in D. UFCS allows you to write a chain of functions that transform an input range, and you can write them in the order that they are applied instead of an awkward set of function calls, i.e. // the awkward way
raiseToThePowerOf( plus( filterIfGreaterThan(myInputRange, 3), 10 ), 2)
// using UFCS to write them in order
myInputRange.filterGreaterThan(3).plus(10).raiseToThePowerOf(2) In this example With this proposal the # could generally mean, take the previous expression and pass it as an argument to the following function
However, as Andrew pointed out, this violates the "only one obvious way to do things", but there may be a way to make this work only one way. That would be to support a function argument annotation (maybe
Actually since the compiler now knows that the
Original example would be
If we wanted to support something like this, I propose that we find as many interesting use cases as we can and come up with a set of generalized function annotations that allow the developer to customize the syntax for how their function is called. I think it's clear that providing a way to customize the syntax can be invaluable in certain cases, so the question is, can we make a simple enough set of rules/annotations that is simultaneously "very useful" without being "very complicated". |
Language features which are just syntactic sugar are rare in Zig, and they tend to only show up where it makes a big difference. Some examples are This proposal so far does not encourage or enable any particular semantics. We've already got method chaining syntax that you can use with your matrix, vector, and complex number types like this: w = x.plus(y).times(x).plus(x);
z = x.plus(y.times(x)).plus(x); and I see nothing wrong with this. I understand that this has the limitation of only working with methods defined in the type of the first parameter. I read the wikipedia article that calls this excessive coupling between classes, but that's a gross overstatement. You'll always be able to use normal function call syntax if you need it, and method syntax is available if the struct author provides it. If your code has a mix of normal function calls and method calls, that might actually be better for readability for the following reason: An import feature of current function call semantics in Zig is the question: where is this function defined? For For more discussion on UFCS in Zig, see #148. |
I have actually been meaning to retract my confidence in this idea. The UFCS syntax has been proving itself sufficient for my use cases. I still haven't finished my parser combinator, but I already think the operator syntax isn't going to make code any more elegant. The #'s were also a way of making the name stand out, make it explicit that it's a function call and not just an identifier. I like sigils, but I concede that we should save them for less trivial things @thejoshwolfe made a good point that naming can make things obvious enough. Even though I was looking for code structure as a way to communicate intent, this did end up being gratuitous sugaring. |
There is discussion happening on the (reopened) UFCS issue, but I think nobody is still making a case for this issue. |
compromise solution const result = blk: {
var x = zigInputRange.filterIfGreaterThan(3);
x = x.plus(3);
x = x.raiseToThePowerOf(2);
break :blk x;
} the goal of the chain syntax is to show the "imperative data flow" from left to right the zig is a compiled language, so the compiler can optimize the code this could also be solved with functional pipes another example: https://nelari.us/post/raytracer_with_rust_and_zig/#living-without-operator-overloading // Zig
if (discriminant > 0.0) {
// I stared at this monster for a while to ensure I got it right
return uv.sub(n.mul(dt)).mul(ni_over_nt).sub(n.mul(math.sqrt(discriminant)));
} infix (ni_over_nt * (uv - (dt * n))) - (discriminant.sqrt() * n) "imperative block" if (discriminant > 0.0) {
return blk: {
// a = (ni_over_nt * (uv - (dt * n)))
var a = dt.mul(n);
a = uv.sub(a);
a = ni_over_nt.mul(a);
// b = (discriminant.sqrt() * n)
var b = math.sqrt(discriminant);
b = b.mul(n);
// a - b
break :blk a.sub(b);
}
} edit: fixed syntax, thanks @leecannon |
@milahu The two given examples are not that different from status quo: const result = blk: {
var x = zigInputRange.filterIfGreaterThan(3);
x = x.plus(3);
x = x.raiseToThePowerOf(2);
break :blk x;
} |
Operator overloading can be very useful, but there is often a concern that it hinders the ability to understand code at first glance: not only may you have to check whether
+
really means add, but it hides a function call. One of Zig's main objectives is clarity, so this makes operator overloading a no-go.Proposal: allow binary functions to be called as operators.
Corollary: allow infix function calls
The
#
is there to make it easier for both humans and computers to parse, and marks the infix function call:Notes:
Kotlin does something similar with unary functions:
kotlinlang.org/docs/reference/functions.html#infix-notation
I'll expand on this at a later date if there is any interest.
The text was updated successfully, but these errors were encountered: