0

Background: I'm working on using scala.js/scalatags together with scala.rx. What I'm trying to achieve is binding values from html inputs to Rx Vars, using operator style. Here's what I'm up to:

implicit class BoundHtmlInput(input: Input) {
  def bindTextTo(v: Var[String]): Input = {
    input.oninput = { (e: dom.Event) => v() = input.value}
    input
  }

  def bindNumberTo(v: Var[Int]): Input = {
    input.oninput = {(e: dom.Event) => v() = input.valueAsNumber}
    input
  }

  def ~>[T](v: Var[T])(implicit ev: T =:= Int): Input =
     bindNumberTo(v.asInstanceOf[Var[Int]])

  def ~>[T](v: Var[T])(implicit ev: T =:= String): Input = 
     bindTextTo(v.asInstanceOf[Var[String]])
}

It works fine for the method calls, but fails for operator ~> call. The error is following:

Error:(43, 70) ambiguous reference to overloaded definition,
both method ~> in class BoundHtmlInput of type [T](v: rx.Var[T])(implicit ev: =:=[T,String])org.scalajs.dom.html.Input
and  method ~> in class BoundHtmlInput of type [T](v: rx.Var[T])(implicit ev: =:=[T,Int])org.scalajs.dom.html.Input
match argument types (rx.core.Var[String])

And I'm not happy about the usage of asInstanceOf either.

I hope this provides enough context. My question is, what is the better way to achieve what I want?

Haspemulator
  • 11,050
  • 9
  • 49
  • 76
  • 1
    Is there a reason why you're using generalized type constraints here over plain old overloading? How about just `def ~>(v: Var[Int]): Input` and `def ~>(v: Var[String]): Input`? – Régis Jean-Gilles Jan 06 '16 at 15:59
  • Then I get: `double definition: def ~>(v: rx.Var[Int]): org.scalajs.dom.html.Input at line 26 and def ~>(v: rx.Var[String]): org.scalajs.dom.html.Input at line 27 have same type after erasure: (v: rx.core.Var)` – Haspemulator Jan 06 '16 at 16:02
  • maybe make just one `~>` and use `TypeTags` as here: http://stackoverflow.com/questions/12218641/scala-what-is-a-typetag-and-how-do-i-use-it – Łukasz Jan 06 '16 at 16:26
  • There is a simple trick to workaround that: add a dummy implicit (just so that the signatures are different): `def ~>(v: Var[Int]): Input` and `def ~>(v: Var[String])(implicit d: DummyImplicit): Input`. If you have more overloads to define one of the other alternatives is to use the *Magnet Pattern* (google it). (EDIT: though you can still use several `DummyImplicit` params as sjrd has just suggested) – Régis Jean-Gilles Jan 06 '16 at 16:33
  • @Łukasz `TypeTags` use reflection, that is banned in `scala.js` – Haspemulator Jan 06 '16 at 16:34
  • ow, thanks, didn't know that – Łukasz Jan 06 '16 at 16:35

1 Answers1

2

Use Var[Int] and Var[String] directly, with dummy implicits to fight off double-def-after-erasure:

def ~>[T](v: Var[Int]): Input =
  bindNumberTo(v)

def ~>[T](v: Var[String])(implicit dummy: DummyImplicit): Input = 
  bindTextTo(v)

You can add as many DummyImplicits as necessary, if you have more types to deal with:

def ~>[T](v: Var[Boolean])(implicit dummy1: DummyImplicit,
    dummy2: DummyImplicit): Input = 
  bindBooleanTo(v)
sjrd
  • 21,805
  • 2
  • 61
  • 91