I'm trying to define a custom operator in Swift with a precedence group higher than a closure. In particular, I want to be able to write:
foo --> bar {
//...
}
The -->
operator returns a function that takes a closure of type () -> Void
as it's sole parameter.
However, I have only been able to get
(foo --> bar) {
//...
}
to work. Is there an operator precedence that can make this work without parentheses?
Here's the precedence group for
precedencegroup LongArrowPrecedence {
associativity: left
higherThan: AssignmentPrecedence
}
infix operator --> : LongArrowPrecedence
Thanks!
We first set up a complete and verifiable example:
precedencegroup LongArrowPrecedence {
associativity: left
higherThan: AssignmentPrecedence
}
infix operator --> : LongArrowPrecedence
func -->(lhs: Int, rhs: Int) -> (() -> ()) -> () {
return { print(lhs+rhs, terminator: ""); $0() }
}
As well as examples of the paranthesis-embraced valid calls using this operator, immediately followed by a call to the closure which -->
returns.
let foo = 1
let bar = 2
// OK
(foo --> bar) {
print(" is the magic number")
} // 3 is the magic number
// OK
((-->)(foo, bar)) {
print(" is the magic number")
} // 3 is the magic number
This doesn't tell us much, but if we study the following failing cases
// ERROR: cannot call value of non-function type 'Int'
foo --> bar {
print(" is the magic number")
} // 3 is the magic number
// ... equivalent to
// ERROR: cannot call value of non-function type 'Int'
foo --> bar({
print(" is the magic number")
}) // 3 is the magic number
We realize that the issue here is not "precedence lower than a closure", but rather that a function-call-argument-clause (a set of parantheses following any postfix-expression) will attempt a call to that postfix-expression, as if the postfix-expression was a method/function/closure. If the postfix-expression is not callable, or if the call within the function-call-argument-clause does not match any overload of the callable, then the compiler will produce an error.
42() // ERROR: cannot call value of non-function type 'Int'
let foo = 42
foo() // ERROR: cannot call value of non-function type 'Int'
func bar() {} // ERROR: argument passed to call that takes no arguments
bar(42)
Hence, the trailing closure supplied to the closure returned from -->
is not relevant here: it's simply an argument to the returned closure, whereas the key issue is that Swift will apply a function-call-argument-clause to the postfix-expression which immediately precedes the clause. In you example, bar
constitutes that postfix expression, and only if you wrap foo --> bar
in parantheses will the combined wrapped expression constitute the postfix-expression onto which the following function-call-argument-clause is applied.
Postfix Expressions
Postfix expressions are formed by applying a postfix operator or other postfix syntax to an expression. Syntactically, every primary expression is also a postfix expression.
Primary Expressions
Primary expressions are the most basic kind of expression. They can be used as expressions on their own, and they can be combined with other tokens to make prefix expressions, binary expressions, and postfix expressions.
You will not be able to circumvent this, as operator precedence is not applicable to the function-call-argument-clause; the latter (and its "precedence") is defined by the grammar of a function call expression.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With