Dotty: Scala Wart: Classes cannot have only implicit parameter lists

Created on 29 May 2017  路  6Comments  路  Source: lampepfl/dotty

Opening this issue, as suggested by Martin, to provide a place to discuss the individual warts brought up in the blog post Warts of the Scala Programming Language and the possibility of mitigating/fixing them in Dotty (and perhaps later in Scala 2.x). These are based on Scala 2.x behavior, which I understand Dotty follows closely, apologies in advance if it has already been fixed


This doesn't work:

@ class Foo(i: Int)
defined class Foo

@ new Foo(1)
res50: Foo = $sess.cmd49$Foo@7230510

@ class Bar(implicit i: Int)
defined class Bar

@ new Bar(1)
cmd52.sc:1: no arguments allowed for nullary constructor Bar: ()(implicit i: Int)$sess.cmd51.Bar
val res52 = new Bar(1)
                    ^
Compilation Failed

But this does:

@ new Bar()(1)
res52: Bar = $sess.cmd51$Bar@467de021

This one straddles the line between "Wart" and "Bug", but definitely should
be fixed so that a class defined with one argument list doesn't magically
sprout two.

language enhancement

Most helpful comment

I'm reopening this issue because the syntax has evolved quite a bit since it was closed and the situation is now "wart-y" again in my opinion:

given Int = 1

class Foo(using x: Int)

new Foo // compiles
new Foo() // compiles
new Foo(using 1) // compiles
new Foo()(using 1) // compiles

The fact that there's some sort of optional empty parameter list is fairly weird, but it gets weirder when combined with the fact that we can now have a regular parameter list after a using parameter list:

class Bar(using x: Int)(y: String) // For a more realistic usecase, see https://github.com/lampepfl/dotty-feature-requests/issues/133

new Bar("") // ERROR: too many arguments for constructor Bar: ()(using x: Int)(y: String): Bar
new Bar()("") // compiles
new Bar(using 1)("") // compiles
new Bar()(using 1)("") // compiles

Here in particular, the fact that one cannot write new Bar("") (or Bar("") using creator application syntax) seems like a real wart to me.

All 6 comments

I think this will be affected by the explicitly proposal.

This bug hit me pretty hard recently, I was looking to curry the type signature of a function with three types. One is supplied by the user, and the other two derived from function arguments. I used code that looks like

  private[Mixer] final class _Mixer[T <: FluidT](implicit ev: ClassTag[T]) {
    def apply[A <: FluidT, B <: FluidT](fst: Fluid[A], snd: Fluid[B])(
        mxe: MixerT[A, B, T]): Fluid[T] =
      Mixer[A, B, T](fst, snd)
  }

 private lazy val _mixer = new Mixer[Nothing]

  def as[T <: FluidT] = _mixer.asInstanceOf[Fluid[T]]

(see http://caryrobbins.com/dev/scala-type-curry/)

Problem is, that the asInstanceOf call doesn't regenerate the ClassTag.

No big deal, I thought, I'll just insert some classtag implicit evidence, and do the new call in as, but now I have to call .apply for it to resolve to the inner method apply, instead of the implicit parameter list of as. I'd much rather the implicits go away at compile time here and allow me to call into the inner method apply.

The only reason I'm doing this hack is so I can have curried type parameter lists.

The only reason I'm doing this hack is so I can have curried type parameter lists.

That's also something we'll hopefully also support directly, if there isn't an issue feel free to create one.

Hi,

I get hit by this one:

trait Encoder[T] {
  def apply(o: T): String
}

object Encoder {
  implicit val intEncoder: Encoder[Int] = _.toString
}

trait Creatable[Resource: Encoder] {
  def create(resource: Resource): Unit = ???   
}

case class PodsOperations() extends Creatable[Int]

The last line won't compile.
The workaround:

case class PodsOperations() extends Creatable[Int]()

Thanks

It turns out this is can alternatively be seen as the problem of old-style implicits taking regular arguments. If we move to given parameters and arguments, all test cases work as expected. E.g. the following compiles:

class TC
implied tc for TC

class Foo given TC

object Test {
  new Foo
  new Foo given tc
  new Foo()
  new Foo() given tc
  Foo()
  Foo() given tc
}

Classes without a leading regular parameter list still assume () as first parameter list. I tried for a while to avoid this but in the end this created more problems than it solved. In particular, with creator applications, it's quite reasonable to demand that Foo() works even if class Foo is parameterless. Otherwise we'd either have to go back to new Foo for this case, or make sure everyone creates their classes with at least a () parameter list. Either choice is unpalatable.

But if creator applications take a () it's consistent to expect that corresponding constructors do so as well.

I'm reopening this issue because the syntax has evolved quite a bit since it was closed and the situation is now "wart-y" again in my opinion:

given Int = 1

class Foo(using x: Int)

new Foo // compiles
new Foo() // compiles
new Foo(using 1) // compiles
new Foo()(using 1) // compiles

The fact that there's some sort of optional empty parameter list is fairly weird, but it gets weirder when combined with the fact that we can now have a regular parameter list after a using parameter list:

class Bar(using x: Int)(y: String) // For a more realistic usecase, see https://github.com/lampepfl/dotty-feature-requests/issues/133

new Bar("") // ERROR: too many arguments for constructor Bar: ()(using x: Int)(y: String): Bar
new Bar()("") // compiles
new Bar(using 1)("") // compiles
new Bar()(using 1)("") // compiles

Here in particular, the fact that one cannot write new Bar("") (or Bar("") using creator application syntax) seems like a real wart to me.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

m-sp picture m-sp  路  3Comments

fommil picture fommil  路  3Comments

mcku picture mcku  路  3Comments

smarter picture smarter  路  3Comments

NightMachinary picture NightMachinary  路  3Comments