Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implicit conversions weirdness

I am trying to understand why exactly an implicit conversion is working in one case, but not in the other. Here is an example:

   case class Wrapper[T](wrapped: T)
   trait Wrapping { implicit def wrapIt[T](x: Option[T]) = x.map(Wrapper(_))

   class NotWorking extends Wrapping { def foo: Option[Wrapper[String]] = Some("foo") }

   class Working extends Wrapping { 
      def foo: Option[Wrapper[String]] = {
        val why = Some("foo")
        why
      }
    }

Basically, I have an implicit conversion from Option[T] to Option[Wrapper[T]], and am trying to define a function, that returns an optional string, that gets implicitly wrapped.

The question is why, when I try to return Option[String] directly (NotWorking above), I get an error (found : String("foo") required: Wrapper[String]), that goes away if I assign the result to a val before returning it.

What gives?

like image 613
Dima Avatar asked May 15 '17 14:05

Dima


People also ask

Are implicit conversions bad?

Implicit conversions are evil, for several reasons. They make it hard to see what goes on in code. For instance, they might hide bad surprises like side effects or complex computations without any trace in the source code.

What is implicit conversion example?

In implicit typecasting, the conversion involves a smaller data type to the larger type size. For example, the byte datatype implicitly typecast into short, char, int, long, float, and double. The process of converting the lower data type to that of a higher data type is referred to as Widening.

How do you avoid implicit conversions in C++?

Keyword explicit tells compiler to not use the constructor for implicit conversion. For example declaring Bar's constructor explicit as - explicit Bar(int i); - would prevent us from calling ProcessBar as - ProcessBar(10); .

How do you use implicit conversions?

An implicit conversion from type S to type T is defined by an implicit value which has function type S => T , or by an implicit method convertible to a value of that type. Implicit conversions are applied in two situations: If an expression e is of type S , and S does not conform to the expression's expected type T .


Video Answer


1 Answers

I don't know if this is intended or would be considered a bug, but here is what I think is happening.

In def foo: Option[Wrapper[String]] = Some("foo") the compiler will set the expected type of the argument provided to Some( ) as Wrapper[String]. Then it sees that you provided a String which it is not what is expected, so it looks for an implicit conversion String => Wrapper[String], can't find one, and fails.

Why does it need that expected type stuff, and doesn't just type Some("foo") as Some[String] and afterwards try to find a conversion? Because scalac wants to be able to typecheck the following code:

case class Invariant[T](t: T)
val a: Invariant[Any] = Invariant("s")

In order for this code to work, the compiler can't just type Invariant("s") as Invariant[String] because then compilation will fail as Invariant[String] is not a subtype of Invariant[Any]. The compiler needs to set the expected type of "s" to Any so that it can see that "s" is an instance of Any before it's too late.

In order for both this code and your code to work out correctly, I think the compiler would need some kind of backtracking logic which it doesn't seem to have, perhaps for good reasons.

The reason that your Working code does work, is that this kind of type inference does not span multiple lines. Analogously val a: Invariant[Any] = {val why = Invariant("s"); why} does not compile.

like image 72
Jasper-M Avatar answered Oct 20 '22 23:10

Jasper-M