Nikita
Nikita

Reputation: 2943

Scala.js implicit conversions don't work... sometimes?

I can't quite understand why implicit conversions from Scala to JS values don't work this way:

Scala Fiddle

import scala.scalajs.js
import scala.scalajs.js.JSConverters._

trait X extends js.Object {
  def map(f: js.Function1[Int, String]) = js.native
  def mapScala(f: Function1[Int, String]) = js.native // just for demo purpose, this is not really part of type X
  def create(maybe: js.UndefOr[Int]) = js.native
}

def test(x: X) {
  // I want these two lines to compile, but they don't
  x.map(a => "yo") // error: missing parameter type
  x.create(None) // error: type mismatch. Found None.type, required: js.UndefOr[Int]

  // This works, but is too verbose
  x.map((a: Int) => "yo") // only if arg type specified like this
  x.mapScala(a => "yo") // because mapScala expects a Scala Function1
  x.create(JSRichOption(None).orUndefined) // because value is manually converted
}

Am I trying to use implicit conversions the wrong way? The implementation of class X is provided externally, in Javascript. In my code I want to pass native Scala values to methods of X, I don't want to do the conversion manually each time.

I know there's an alternative way for Scala.js – to pimp my type X like this, but I'm trying to avoid that because it's more boilerplate and a lot more objects will be instantiated at runtime that way. Regardless of that, I still do want to understand why exactly my code isn't working.

Upvotes: 2

Views: 221

Answers (1)

gzm0
gzm0

Reputation: 14842

The reason the map call does not work is a limitation in Scala's type inferencer: It needs to resolve the full type before looking for implicit conversions. However, Scala 2.12 brings SAM treatement just like Java. Roughly that means that you can write a lambda in stead of an anonymous class implementing an interface with a single method.

So x.map(a => "yo") will expand into:

// Never (!) write this yourself.
x.map(new js.Function1[Int, String] {
  def apply(a: Int): String = "yo"
})

Which the Scala.js compiler handles separately. You can enable SAM treatment in Scala 2.11 with the -Xexperimental flag.

The reason the create call does not work is that there is no implicit conversion (in the Scala.js standard library) from Option[T] to js.UndefOr[T]. There is an implicit conversion to JSRichOption[T] which enables the orUndefined method. You should write:

x.create(None.orUndefined)

Or in this case, you can simply write:

x.create(js.undefined)

Upvotes: 2

Related Questions