41

Is there a best practice for one over the other? I've been reading the Scala book by Odersky, et al. and it seems like infix is used for a lot of the Collections API functions, whereas dot is reserved for programmer-defined functions.

Martin Schröder
  • 4,176
  • 7
  • 47
  • 81
Kevin Li
  • 1,540
  • 2
  • 17
  • 23

4 Answers4

55

I personally do not have any hard and fast rules for this, but I tend to use infix notation only with symbolic method names, and dot notation for alphanumeric ones.

Infix notation makes it cumbersome to modify code later. Here are some examples.

Imagine you have this line of code:

xs filter { f } map { g }

Suppose at some latter point in time you need to add a toList at end. You put it so:

xs filter { f } map { g } toList

This may cause semicolon inference issues. To avoid these issues, you either put a semicolon at end, or put a new line. Both options are ugly, in my opinion. To avoid all this nonsense, I prefer to go with xs.filter(f).map(g). It's always easier to refactor with this syntax.

Another example: Say I have the following in my code:

if(foo contains bar) { ..

Say, I need to negate the condition. If I modify it as follows:

if(!foo contains bar) { ..

Bummer. This gets parsed as (!foo).contains(bar). Not what we wanted.

Or suppose you need to add a new condition in addition, and you modify it so:

if(foo contains bar && cond) { ..

Another bummer. This gets parsed as foo.contains(bar.&&(cond)). Not what we wanted, again.

Of course, you could add a bunch of parentheses around, but that would be ugly and hard to read/edit as compared with dot notation.

Now, all of what I said above applies to symbolic method names too. However symbolic methods look unnatural when used with dot syntax, and so I prefer the infix syntax for them.


One exception to the guideline above: Internal DSLs. They are usually crafted with care so as not to cause parsing issues when written in the manner prescribed in their documentation/examples (which usually uses infix notation).

missingfaktor
  • 90,905
  • 62
  • 285
  • 365
  • 7
    This same logic can also be used in favour of infix notation, as it gives an additional mechanism for controlling precedence (in addition to choosing operators). AND it makes the precedence more visually obvious. For example, try rewriting your first example with true functions and not just lifted methods: `(xs filter f map g).toList` is still far cleaner and more obvious than `xs.filter(f).map(g).toList)` – Kevin Wright Apr 30 '12 at 15:01
  • 2
    Putting parentheses around requires non-linear editing, one more reason to avoid infix notation. Dropping the braces around doesn't help the case either. – missingfaktor Apr 30 '12 at 15:12
  • 2
    Dropping parenthesis in there is the whole point. When combined with the relative "strength" of operators and the use of parentheses/braces, then having the ability to *also* use infix vs. dot notation to control precedence is a very powerful tool for keeping code clean. – Kevin Wright Apr 30 '12 at 19:34
  • 1
    And, yes, un-dotted POSTfix methods are a Bad Thing(tm). But it's a bit of a straw man fallacy to use them in criticising INfix method calls. – Kevin Wright Apr 30 '12 at 19:37
  • 1
    My point was that with infix notation modifying code becomes harder, and I have well provided many arguments _and_ examples in favor of that claim. If you read properly, I am not using postfix notation to argue against infix notation, and that makes above comment of yours a strawman. – missingfaktor Apr 30 '12 at 19:42
  • You see, I am not fond of the extra noise - all the dots and parentheses, but I am not fond of the issues I discussed either. That's why, since some time, I use infix notation very sparingly. – missingfaktor Apr 30 '12 at 19:47
  • 3
    `someExpression` => `someExpression.newPart` is not significantly better than `someExpression` => `(someExpression).newPart` In both cases you must add `.newPart`. Admittedly, you have to add the parenthesis in the second case, but you likely started with a lot less of them in the first place. The straw man was to justify the former approach with the "This may cause semicolon inference issues." explanation - which is a symptom of the added postfix call, not the original expression. – Kevin Wright Apr 30 '12 at 19:49
  • 1
    @missingfaktor I have grown to try and avoid such infix calls for exactly the reasons you describe. – Jean-Philippe Pellet May 12 '12 at 17:12
12

It's a matter of personal preference. Your decision to use one style or the other should be based on what makes your code the most readable.

But note that the ability to leave off the dot and parentheses is limited only to certain syntactic constructions, so sometimes you just have to fall back to using them.

dhg
  • 52,383
  • 8
  • 123
  • 144
  • 6
    Note also that `a.op(b)` is longer than `a op b` but shorter than `(a op b)`. Minor difference but it can be part of the "most readable" consideration. – Rex Kerr Apr 19 '12 at 18:17
  • You can use a?b instead of a.?(b) or a ? b if you need to save every byte. :) Of course, the number of methods, nameable like this, is somehow limited. – user unknown Apr 20 '12 at 10:36
4

There is a good style guide in official Scala site documentation that describes the proper usage of infix notation over dot notation.

Suffix Notation:

names.toList
// is the same as
names toList // Unsafe, don't use!

Arity-1:

// right!
names foreach (n => println(n))
names mkString ","
optStr getOrElse "<empty>"
// wrong!
javaList add item

Higher-Order Functions:

// wrong!
names.map (_.toUpperCase).filter (_.length > 5)
// right!
names map (_.toUpperCase) filter (_.length > 5)

Symbolic methods/Operators:

// right!
"daniel" + " " + "spiewak"
// wrong!
"daniel"+" "+"spiewak"
Saeed Zarinfam
  • 9,818
  • 7
  • 59
  • 72
0

I have found that using infix notation for map works nicely when I am creating cartesians with the cats library. e.g.:

(fetchIt(1) |@| fetchIt(2) |@| fetchIt(3)).map(MyCaseClass)

you can get rid of the surrounding parentheses like so:

fetchIt(1) |@| fetchIt(2) |@| fetchIf(3) map MyCaseClass

and in my view the second variant reads nicer. A matter of taste I suppose. Just wanted to add my two cents' worth.

The above works because | in "|@|" has higher precedence than m in "map". Read this part of Scala lang specification to find out more detail:

If there are several infix operations in an expression, then operators with higher precedence bind more closely than operators with lower precedence.

http://scala-lang.org/files/archive/spec/2.12/06-expressions.html#infix-operations

Peter Perháč
  • 20,434
  • 21
  • 120
  • 152