Which stdlib functions and types should be marked as `infix`?

Scala 3.4 introduces warnings on alphanumeric method application in infix positioning if such methods are not explicitly marked with infix.

I think equals should be marked as infix, as it is naturally used in infix positioning.
Do you agree? What other stdlib functions/types do you think should be marked as infix?

Related issue:

4 Likes

I agree for equals, I’d also nominate zip and lazyZip, which I almost always use infix.

I also like to use union as infix.

In general operations that are symmetrical or associative, as there is no benefit to clarifying what are the operands.

Things like map, foreach, etc, I’d be against putting as infix

7 Likes

I think all kinds of things are naturally used in infix position. drop, append (on java.lang.StringBuilder), orElse on Option, to and until to create ranges, contains on sets and maps, etc. etc. etc…

I really don’t think the change is very well-conceived. Adding one bit of extra information to remember for every alphanumeric method is a bad way to solve the possible problem of people abusing the feature of infix method names. This is the kind of thing linters should cover, not the compiler. This is particularly true given that no general statement can be made about Java libraries. For instance "salmon" charAt 3 seems pretty bad to me, because charAt acts like apply and thus should look like it: "salmon".charAt(3). On the other hand, date plus duration reads far better than date.plus(duration).

For everything I control, I expect I will just sed 's/\(infix\)\{0,1\} def\b/infix def/' so I don’t have to worry about it, assuming I can’t just turn the warning off.

6 Likes

Agreed, particularly since (at least for me) the decision to use infix or not depends a lot on what’s going on where it’s called, rather than the details of the method itself.

I find this to be nice and readable:

list.reduce(_ foo _).bar // compared to list.reduce(_.foo(_)).bar

I find this significantly less so:

(a foo b).bar // compared to a.foo(b).bar

Counterpoint: I almost never use those methods infix :slight_smile:


I would be very hesitant to make any claims about what methods are more likely to be used infix, because wherever the cutoff ends up, it’s going to piss somebody off. To be honest, I kind of think having to explicitly annotate infix-capable method definitions solves a problem that isn’t actually a problem in practice.

5 Likes

I am curious - even if there is an agreement on a list, what good will it be? Given stdlib needs be consumed on 2.13, how will be “marked as infix” be handled? (See also Scala 3 specific stdlib improvements which muses mostly about inline in stdlib).

Scala 2 lets you write infix def under -Xsource:3 (Allow soft keywords `open` and `infix` under -Xsource:3 by smarter · Pull Request #9580 · scala/scala · GitHub) although it’s just ignored so the information isn’t available when scala 3 reads a scala 2 jar. There’s an ongoing experiment where the scala 2 standard library is compiled to tasty files by scala 3 (see Publish `scala2-library-tasty-experimental` by nicolasstucki · Pull Request #19588 · scala/scala3 · GitHub) which would let us pick up that information.

Incidentally, is anyone actually enjoying infix? Does it make people’s code better?

It’s partly in the nature of the feature that only the pain points tend to get attention, but still, approximately every comment I’ve seen since the feature has hit has been someone inconvenienced in some way. For instance, the Scala 2 libs might get tasty annotations, but the Java standard libs won’t, Apache Commons Math won’t, ImgLib2 won’t, etc…

Is there any way we can know semi-objectively whether it’s making the Scala experience better (and for whom, if the experience differs between people)?

1 Like

Would it be possible to make a function infix on import, instead of where the method is defined? That might make DSL design - where infix really shines - more natural while eliminating the dissonance when the infix is used sparingly.

I think if it wasn’t bundled with all the Scala 3 features, it would have never gone passed the SIP committee. I’m a heavy DSL user and failing to see any benefit. Only increases technical dept.

5 Likes

As I understand it, allowing it to be set on import would undermine the justification for the feature - to let library maintainers dictate if a method could be called infix or not.

I don’t agree with the premise that infix is going to make code more regular, but that’s my understanding of the reason for it’s existence.

2 Likes

I think the answer is: Basically no user code should be infix, except if it’s part of a particular fluid DSL. That’s the only way to enforce syntactic uniformity.

Outside of DSLs, the only exception I see is operators that are known throughout the system such as eq, ne, min, or max. But even for these it’s debatable.

Note that you can always write an operator between backquotes. E.g.

   myList `contains` myElement

That’s a bit more to type, but much clearer.

1 Like

I am afraid I do not want to be enforced this decision in my code - and I think I am not alone.

I find 0 until 10 very nice, 0.until(10) outright ugly and 0 `until` 10 unnatural. When the operation acts like an operator, only there is no suitable symbol for it, I really prefer to use infix syntax - like dot or union.

5 Likes

I really dislike the backticks and find them to be even more distracting then the braces { } and ( ) we tried so hard to eliminate (which was a big improvement btw). The solution with infix is just fine. Let’s keep it.

1 Like

Right. I guess until and to would have to be included in the list of language-wide quasi-operators. They are effectively syntax.

2 Likes

Right, but – so what?

I mean, I get syntactic uniformity as a pure ideal. But after 15 years in the trenches, programming Scala full-time on a variety of projects with a variety of coding styles, I honestly find it to be one of those ideals that is intellectually pretty but counter-productive.

Getting back to @Ichoran’s original point, it looks to me like the infix requirement has been a net negative: it enforces a uniformity that isn’t actually very helpful in practice, while eliminating a flexibility that is useful for helping create readable, maintainable code.

Yes, that flexibility needs to be used judiciously at the call sites. But that’s true of absolutely everything. I have a button somewhere that says, “There is not now, and never will be, a programming language in which it is the slightest bit difficult to write bad code” – I think that’s pretty much a truism. But in practice I haven’t often seen infix syntax misused in ways that are particularly confusing, and I often have seen it used in ways that enhance readability.

Overall, I think the infix restriction is looking to be a net negative in terms of code maintainability – and to me, maintainability outweighs everything else.

4 Likes

Hard disagree. The syntactic non-uniformity is one of the first reasons not to adopt Scala. I have learned that from interacting with many people outside the Scala community. In retrospect, allowing alphanumeric infix methods was a mistake. We would have been better off by defining a fixed list of words in the syntax that behave like operators. Now we have to live with it and are slowly migrating away from it. And there will be no turning back, as far as I am concerned.

That’s rather at odds with my experiences, teaching and evangelizing Scala for a variety of organizations.

But even if I were to grant that, Scala 3 made things far worse, by introducing the braceless style. I’ve never once heard anyone even blink at the infix notation, but people constantly talk about Scala 3 being a completely different and incompatible language because the new braceless idiom looks so wildly different from Scala 2 code, and in practice encourages less uniformity in coding style. (Since many of us find the new style hard to read, and use it sparingly.) I spend a lot of time convincing people that it’s even the same language, much less that it’s largely upward compatible.

So even if I agreed with the point (which I don’t – I have yet to hear a single person talk about syntactic inconsistency as a reason they are reluctant to use Scala), we aren’t by any stretch of the imagination seriously trying to improve that.

6 Likes

I’m personally in favor of limiting infix alphanumeric methods overall.

There are some valid use cases where the method is commutative or symmetric in some way, like eq or max, that I agree work fine infix. But the Scala community as a whole has gone way to far in the past.

Even things like 1 to 10 and 1 until 10 are weird to me. Range(1, 10) and Range.Inclusive(1, 10) are my preferred way of spelling those, and are IMO a lot clearer than trying to remember which of to and until are right-inclusive vs right-exclusive when the english meaning of the two words is exactly the same.

I know not everyone agrees with me. It’s subjective, like Ruby vs Python. And in this case I fall squarely in the “Python” camp of parenthesized method calls rather than Ruby’s whitespace-delimited method calls

If people agreed with me on this and other things, I wouldn’t have had to spend the last decade building my own Scala software ecosystem from top-to-bottom. And yet here I am, perpetually fighting the current in a sisyphean struggle

3 Likes

I think we all agree that infix methods can be abused
And that a language without any infix methods (or only symbolic ones) is simpler, but that they are currently part of Scala
Furthermore we can maybe agree that some infix methods make code easier to read, and understand, sometimes (But maybe not on which methods and which times)

And we seem to go around in circles arguing that “1. is more important than 3.”, that “2. is true”, or other such things

But these are red herrings, the question is only:
“Given the language as it stands, with it’s current libraries and features, is the infix keyword a worthwhile means to address these points ?”

We can simplify it further:

  1. People experienced in Scala are not abusing infix methods (Though they might have learned it the hard way)
  2. Infix non-symbolic methods have to stay in the language in some way, so all their technical complexities will remain (i.e. we cannot simplify the spec/compiler, only make them as distant as possible from users, see XML literals in Scala 2)

Therefore, the question becomes “Is the infix keyword a good way to make sure new Scala developers do not abuse infix methods ?”

And I believe it fails in that regard:

  1. Users will have one more thing to worry about “Can this method be called infix ?” (which is distinct from the question of “should”), through one more keyword and some extra rules (symbolic methods do no need infix)
  2. If they are not taught about infix methods at all, the keyword does not change the situation
  3. A keyword does not teach users why something is bad, it just makes it slightly harder to do
  4. We should teach users why infix is dangerous, but this is the case regardless of whether the infix keyword exists !
  5. New users write a lot of the code they use themselves (you don’t usually start learning with a library), it is therefore easy for them to add infix in front of any method they want. They’ll just be frustrated later when using libraries and all of a sudden the bad habit they had is no longer possible, they might learn then the lesson, or just open an issue/PR to add infix

In summary, I believe the infix keyword is a bad solution as it’s trying to fix imperfect documentation/learning material through an addition to the language (and would then require more documentation and learning material)

3 Likes

I’m not sure everyone would agree upon this.

Maybe some people see input.split("\\s+") to Vector or input endsWith "oops!" and recoil. Maybe others are like, hey, that’s a really clean and clear look–let’s use that! (There is no “learning the hard way” because it is clean and clear. They’re not wrong. But it might not be to everyone’s preference.)

Personally, I find that infix coheres very nicely with fewer braces (both are less pointless visual clutter), so I’m using it way more than I used to. But it could well be that my style would drive Li Haoyi absolutely batty.

Anyway, I am not sure that arguments going round and round even in better-focused form like you suggest are all that useful either. If we don’t have any way to get data about how painful it is, if it’s working, or if anyone likes the impact of the feature itself (rather than liking the goal of having less infix)

Well that just tells me you don’t use ranges. When you want to use something a lot, you call it P or T or somesuch. I think it’s a great insight that it’s fine to make the critical common stuff super-easy to use.

If one uses a lot of ranges, one knows what to and until mean, and one doesn’t want it longer. (It could be different, but not longer.)

2 Likes