Home > Blockchain >  Why can I pass operators directly to functions, but have to surround them with parentheses everywher
Why can I pass operators directly to functions, but have to surround them with parentheses everywher

Time:11-09

As we know, you can pass operators directly to functions like this:

func foo(_ x: (Int, Int) -> Int) {
    
}

foo( )

However, when I tried to do this in an assignment context, it doesn't work:

let f: (Int, Int) -> Int =  

It gives two errors:

Expected initial value after '='

Unary operator cannot be separated from its operand

I worked out that I have to do:

let f: (Int, Int) -> Int = ( )

How exactly does Swift parse each of these cases, allowing me to omit brackets in one case, but not the other?


Note that I'm not asking about the rationale behind Swift being designed this way. I'm just asking about the grammar production rules that produces this behaviour.

On Swift.org, I've been able to find this production rule that says operators are function call arguments:

function-call-argument → operator | identifier ':' operator

However, I've not been able to find production rules for ( ). I started with "parenthesised expressions", which is a pair of parentheses surrounding an "expression". but "operator" is apparently not a kind of "expression".

CodePudding user response:

I found Swift AST Explorer, which allows me to inspect the AST of Swift code. From its GitHib page, it seems to use the same library that the Swift compiler uses to parse Swift code (lib/Syntax).

Using Swift AST Explorer, the ( ) part of let x: (Int, Int) -> Int = ( ) parses to:

TupleExpr
  (
  TupleExprElementList
    TupleExprElement
      IdentifierExpr
         
  )

So apparently, ( ) here is a 1-tuple! And is classified as an "identifier expression". This seems to also explain why operators in tuple elements don't need parentheses:

let f: ((Int, Int) -> Int, (Bool) -> Bool, (Int) -> Int) = ( , !, -)

After a bit more searching, I found ExprNodes.py, which seems to be one of the files from which some of the APIs in lib/Syntax is generated. In there, I saw that SpacedBinaryOperatorToken is a child of IdentifierExpr, so that's probably what is being parsed as.

Node('IdentifierExpr', kind='Expr',
     children=[
         Child('Identifier', kind='Token',
               token_choices=[
                   'IdentifierToken',
                   'SelfToken',
                   'CapitalSelfToken',
                   'DollarIdentifierToken',
                   'SpacedBinaryOperatorToken',
               ]),
         Child('DeclNameArguments', kind='DeclNameArguments',
               is_optional=True),
     ]),

Interestingly, unary operators such as ! and - are also being parsed as "binary operators".

CodePudding user response:

the " " is an infix operator, therefore I guess it has to be "between" some defined types (eg. Int and Int), and you cannot just declare it standing alone. You can define your own infix operator, lets say with this code:

    infix operator ~
func ~(lhs: Double, rhs: Double) -> Double {
    return lhs * lhs   rhs * rhs
    }

let val1: Double = 2
let val2: Double = 3

let squareSum = val1 ~ val2
print(squareSum)

So I guess the key to your answer is that " " is not just some regular function, but an infix operator and defining it with parenthesis (x) helps the compiler to understand it does not stand in between something. Hopefully this answer helps you with some guideline where to look for further explanation if you wish.

  • Related