When to use parenthesis in Scala infix notation

前端 未结 4 1993
名媛妹妹
名媛妹妹 2020-11-28 13:09

When programming in Scala, I do more and more functional stuff. However, when using infix notation it is hard to tell when you need parenthesis and when you don\'t.

相关标签:
4条回答
  • 2020-11-28 13:14

    The spec doesn't make it clear, but my experience and experimentation has shown that the Scala compiler will always try to treat method calls using infix notation as infix operators. Even though your use of mkString is postfix, the compiler tries to interpret it as infix and so is trying to interpret "map" as its argument. All uses of postfix operators must either be immediately followed by an expression terminator or be used with "dot" notation for the compiler to see it as such.

    You can get a hint of this (although it's not spelled out) in A Tour of Scala: Operators.

    0 讨论(0)
  • 2020-11-28 13:25

    This is what I put together for myself after reading the spec:

    • Any method which takes a single parameter can be used as an infix operator: a.m(b) can be written a m b.
    • Any method which does not require a parameter can be used as a postfix operator: a.m can be written a m.

    For instance a.##(b) can be written a ## b and a.! can be written a!

    • Postfix operators have lower precedence than infix operators, so foo bar baz means foo.bar(baz) while foo bar baz bam means (foo.bar(baz)).bam and foo bar baz bam bim means (foo.bar(baz)).bam(bim).
    • Also given a parameterless method m of object a, a.m.m is valid but a m m is not as it would parse as exp1 op exp2.

    Because there is a version of mkString that takes a single parameter it will be seen as an infix opertor in fromFile(file) mkString map caesar(k)_. There is also a version of mkString that takes no parameter which can be used a as postfix operator:

    scala> List(1,2) mkString
    res1: String = 12
    
    scala> List(1,2) mkString "a"
    res2: String = 1a2
    

    Sometime by adding dot in the right location, you can get the precedence you need, e.g. fromFile(file).mkString map { }

    And all that precedence thing happens before typing and other phases, so even though list mkString map function makes no sense as list.mkString(map).function, this is how it will be parsed.

    0 讨论(0)
  • 2020-11-28 13:32

    Here's a simple rule: never used postfix operators. If you do, put the whole expression ending with the postfix operator inside parenthesis.

    In fact, starting with Scala 2.10.0, doing that will generate a warning by default.

    For good measure, you might want to move the postfix operator out, and use dot notation for it. For example:

    (fromFile(file)).mkString map caesar(k)_
    

    Or, even more simply,

    fromFile(file).mkString map caesar(k)_
    

    On the other hand, pay attention to the methods where you can supply an empty parenthesis to turn them into infix:

    fromFile(file) mkString () map caesar(k)_
    
    0 讨论(0)
  • 2020-11-28 13:34

    The Scala reference mentions (6.12.3: Prefix, Infix, and Postfix Operations)

    In a sequence of consecutive type infix operations t0 op1 t1 op2 . . .opn tn, all operators op1, . . . , opn must have the same associativity.
    If they are all left-associative, the sequence is interpreted as (. . . (t0 op1 t1) op2 . . .) opn tn.

    In your case, 'map' isn't a term for the operator 'mkstring', so you need grouping (with the parenthesis around 'fromFile(file) mkString')


    Actually, Matt R comments:

    It's not really an associativity issue, more that "Postfix operators always have lower precedence than infix operators. E.g. e1 op1 e2 op2 is always equivalent to (e1 op1 e2) op2". (Also from 6.12.3)

    huynhjl's answer (upvoted) gives more details, and Mark Bush's answer (also upvoted) point to "A Tour of Scala: Operators" to illustrate that "Any method which takes a single parameter can be used as an infix operator".

    0 讨论(0)
提交回复
热议问题