32

As far as I understand, implicit conversions can result in potentially hard to understand code, or code suffering from other problems (perhaps even bugs?), which is why they require explicit enabling in order to be used in code without getting warnings.

However, given that implicit conversions are in big part (if not most of the time) used for wrapping an object with an object of another type, and so are implicit classes—I'd appreciate you correcting me if I'm wrong—, why do the former require the import of scala.language.implicitConversions but the latter do not?

object Main extends App {
  implicit class StringFoo(x: String) {
    def fooWithImplicitClass(): Unit =
      println("foo with implicit class")
  }
  // => silence.

  "asd".fooWithImplicitClass()

  /************************/

  class Foo(x: String) {
    def fooWithImplicitDef(): Unit =
      println("foo with implicit def")
  }
  implicit def string2Foo(x: String) = new Foo(x)
  // => warning: implicit conversion method string2Foo should be enabled

  "asd".fooWithImplicitDef()
}
Erik Kaplun
  • 37,128
  • 15
  • 99
  • 111

2 Answers2

21

Implicit classes effectively only add new methods (or traits), and they are only ever used when these added methods are called (or the implicit class is used explicitly, but this rarely happens in practice). Implicit conversions to existing types, on the other hand, can be invoked with less visibility to the programmer.

Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
  • 6
    Do you mean that it's because the implicit conversion kicks in not only when a method non-existent in the "original" type is used, but also when an object of the "implicit type" is requested, e.g. when passing `TypeA` instead of `TypeB` and a conversion `TypeA -> TypeB` exists? – Erik Kaplun Sep 20 '13 at 11:26
  • Yes, that's what I mean. – Alexey Romanov Sep 20 '13 at 11:37
  • I am not sure to get the argument... Even with the first (implicit class) version, a `String` can be passed where a `StringFoo` is expected, much like a `String` can be passed where a `Foo` is expected in the second version. My understanding is that both versions are strictly equivalent (except, as it seems, regarding said warning) – Régis Jean-Gilles Sep 20 '13 at 13:24
  • 1
    @RégisJean-Gilles: what Alexey meant is that `StringFoo` is a type specifically dedicated for the purpose of amending an existing type with extra methods, whereas implicit conversions allow for conversions from type `A` to a completely unassociated `B` without `B` having been created specifically for that purpose. (but I'm still seeing if even better explanations pop up, for now) – Erik Kaplun Sep 20 '13 at 14:03
  • 2
    @Erik a quick experiment in the REPL shows that implicit classes perform the same conversion, so I don't understand this argument http://ideone.com/m025gQ – Luigi Plinge Sep 20 '13 at 14:46
  • 1
    So if I understand correctly the argument here is that of the greater versatility (and thus greater risk surface) of implicit conversions (in the general sense)? If so, it could be argued that the compiler could (should?) refrain from emitting a warning if said implicit conversion is defined in the same scope as the class it converts to (given that in this case we are in the exact same situation as when using an implicit class). I guess it was just easier to emit a warning indiscriminately. – Régis Jean-Gilles Sep 20 '13 at 15:52
  • @LuigiPlinge: you cannot convert from type `A` to type `B` with an implicit class `C` (assuming `C ≠ B`) but instead only `A → C`; @RégisJean-Gilles: I tend to agree with the propsal :) – Erik Kaplun Sep 20 '13 at 15:52
  • But it's pretty much like saying that you cannot convert from type `A` to type `B` with an implicit conversion from type `A` to class `C` ^^ Which is quite tautoligical – Régis Jean-Gilles Sep 20 '13 at 15:54
  • The point is that an `ImplicitClass` is very likely to be created for the sole purpose of adding methods to an `OrigType`, where as an implicit conversion to an `AnotherType`, where this type is a completely unrelated type, is more magical. Basically it boils down to which use case is more likely, and trying to discourage one particular type of use case. – Erik Kaplun Sep 20 '13 at 18:13
  • 1
    @RégisJean-Gilles I agree about not needing to emit warning in this case. On the other hand, I also think it would make sense to emit warning for implicit classes which extend other classes implement traits, since they are also conversions to preexisting types. I'd guess it was overlooked because such implicit classes are rare (I've never seen one), but they are legal. – Alexey Romanov Sep 20 '13 at 18:18
  • Isn't this answer just plain wrong? Implicit classes *are* invoked as implicit conversions to preexisting classes/traits just like implicit functions are, and both can be side-effectful too. http://ideone.com/1wwCoA – aij Mar 22 '17 at 20:49
  • @aij For the first: see comments above. For the second: this _really_ doesn't happen in practice and there is no reasonably useful way for compiler to detect if it does. – Alexey Romanov Mar 22 '17 at 21:40
0

IMO, there is no fundamental difference between an implicit class and an implicit conversion as regards the confusion they might potentially incur, thus both should have been warned.

But by defining a class as implicit, it's like explicitly suppressing the warning by telling the compiler that "I'm an adult, I know what I'm doing. THIS CLASS IS INTENDED TO BE USED THIS WAY (possibly as an extension wrapper)." Therefore no warnings will be given since you, as the creator of the class, have made it clear that using implicitly is how this class is supposed to work or how the class is allowed to be used, compiler should certainly trust you.

You can, on the other hand, convert an object to whichever class by using implicit conversion, no matter if the target class is intended to be used implicitly. This is the source of many troubles, and also what Scala is trying to prevent.

Lifu Huang
  • 11,930
  • 14
  • 55
  • 77
  • The problem with that argument is that the class being converted to *doesn't* need to be defined as implicit in *either* case. Eg: http://ideone.com/1wwCoA – aij Mar 22 '17 at 20:36
  • But I think in the code sample you provided, that string IS converted to `StringToFoo`, right? https://ideone.com/sBKryP – Lifu Huang Jan 17 '18 at 02:14