Are infix objects intended?

in scala and dotty, given

package foo {
  object Bar {
    def apply(i: Int, j: Int, k: Int) = i + j + k
  }
}
object Test {
  val x = foo Bar (1, 2, 3)
}

Bar is a perfectly cromulent infix operator.

That feels wrong. It sounds reasonable if infix were limited to cases where the left part of the infix is an expression, and can’t be a package.

It seems that this is also the intention of the spec, which speaks in terms of e0;op1;e1 which seems to imply that the intent is the e's are expressions. But I’m not sure if that’s also the letter.

What are peoples thoughts on this? Is this according to spec? Should it be? And if not, what should be done about it?

Relevant specs are:

6.12 Prefix, Infix, and Postfix Operations

PostfixExpr     ::=  InfixExpr [id [nl]]
InfixExpr       ::=  PrefixExpr
                  |  InfixExpr id [nl] InfixExpr
PrefixExpr      ::=  [‘-’ | ‘+’ | ‘!’ | ‘~’] SimpleExpr

9.4 Package References:

A reference to a package takes the form of a qualified identifier. Like all other references, package references are relative. That is, a package reference starting in a name p will be looked up in the closest enclosing scope that defines a member named p.

6.4 Designators

A designator refers to a named term. It can be a simple name or a selection .

It is a bit ambiguous since in Scala you can’t assign a package (reference) to a val or pass it around to a function, so it feels odd that it can act as a receiver of a method.

1 Like

What’s the problem? It’s just syntax. Why do you care where I define my objects?

scala> :pa
// Entering paste mode (ctrl-D to finish)

package object p { def unary_! = 42 ; def *(n: Int) = "p"*n ; object + { def apply(i: Int) = ('p' + i).toChar } } 

// Exiting paste mode, now interpreting.


scala> !p
res1: Int = 42

scala> p * 2
res2: String = pp

scala> p + 1
res3: Char = q

Agree with @som-snytt here. What problem is this capability causing? I don’t think syntax should be disallowed for the sake of disallowing it. In cases where there is an actual case that becomes undetectably incorrect then I’d get it. Like adapted args - though I’m a fan, there are a couple of edge cases where it causes a problem (personally I don’t think there ought to be a difference between (A, B) => C and ((A, B)) => C, but everyone else seems to hate it).

So is there a case where foo Bar (1, 2, 3) should mean something else? Or a reason it shouldn’t be allowed?

1 Like

It’s just wrong to have an object with an apply method at the top level of a package be an infix operator.

Morally. Ethically. And definitely aesthetically.

import collection._

mutable HashSet (1, 2, 3)

Is (only) syntax only a parent could love.

1 Like

You just wanted an excuse to say cromulent didn’t you?

I agree it’s a bit weird… but not problematic. I’m more worried about using >1-ary methods as infix operators.

The question is not what the compiler should allow, it’s what it should disallow. The infix rule is just a rule of syntax.

That’s just, like… your opinion, man :smiley:

package object dont {
  object messWith {
    def apply[T](t: T): String = s"don't mess with $t"
  }
}
import scala.language.postfixOps

val peoplesDSLs = "people's DSLs"

val Texas = "Texas"

implicit class `Seriously!`(self: String) {
  def ! = s"$self!"
}

dont messWith peoplesDSLs
dont messWith Texas!
3 Likes

I agree it’s a bit weird… but not problematic. I’m more worried about using >1-ary methods as infix operators.

There’s a PR for that: https://github.com/lampepfl/dotty/pull/4311

It’s currently on hold because we’d need some deprecations in the 2.x series to make migration smooth. As a start, the new 2.13 collections have deprecated multi-argument infix operators such as xs += (1, 2, 3). But we’d need to put the deprecations in the compiler so that they can be applied everywhere.

Not even "" + () does what you expect. That is, if you expect to concatenate arbitrary values to a String with String_$plus.

Before someone says it’s fixed in Dotty, there’s a PR for Scala where I noticed usages such as

nextCase APPLY ()

(which will no longer work). In the PR, I rely on the spec language about multi-arg infix to assert that it doesn’t apply to empty args. To me, the eye opener is that these idioms become naturalized.

It also occurs to me that the next iteration of the language should be called neither Scala nor Dotty, but Scotty, after the chief engineer on the Enterprise, and maybe with a nod to kilt-wearers in the community. I think it would go a long way toward advancing Scotty in the enterprise, because it has the aura of folks who get things done. Let’s not forget that the real context of “Beam me up, Scotty!” is that the transporter is malfunctioning and the dilithium crystals are jury-rigged like a Christmas tree.

This also allows expressions like a + b = c , and even if this feature may look useless, I like to know that I can do that (however probably not with package objects)

    class Foo {
      object + {
        def update(arg2: Bar, value: Baz): Baz = {
          println(s"assigning: a + b = c: ${Foo.this} + $arg2 = $value")
          value
        }
      }
    }

    class Bar

    class Baz

    // this code is correct, prints something like
    // assigning: a + b = c: crafts.Foo@33723e30 + crafts.Bar@64f6106c = crafts.Baz@553a3d88
    new Foo + new Bar = new Baz
2 Likes

How does this not do what I would expect?

"" + ()

I expected that to concatenate an empty String with a Unit converted to String, giving “()”, and it appears that’s what it does. What else would some one expect?

Two parses I could imagine is that scala parses the () as an empty parameter list, and unit insertion does the rest, and that scala parses the () as unit, and infix notation does the rest.

Only now I realize (timely, I know) that () is not a valid identifier.

Right. It’s inserting a unit into an empty parameter list.

There seem to be two kinds of warnings in Scala: The “this is unlikely to be what you want” warnings and others.

**Welcome to Scala 2.12.4 (OpenJDK 64-Bit Server VM, Java 1.8.0_181).
Type in expressions for evaluation. Or try :help.

“” + ()
:12: warning: Adaptation of argument list by inserting () is deprecated: this is unlikely to be what you want.
signature: String.+(x$1: Any): String
given arguments:
after adaptation: String.+((): Unit)
“” + ()
^
res0: String = ()**