Scala: Enforcing A is not a subtype of B -
i trying overload method based on whether or not parameter extends given class, , have been having trouble. using an approach miles sabin, produced following code:
object extendedgenerictypes { trait <:!<[a, b] // encoding "a not subtype of b" // use ambiguity rule out cases we're trying exclude implicit def nsubambig1[a, b >: a]: <:!< b = null implicit def nsubambig2[a, b >: a]: <:!< b = null // implicit substitutions implicit def nsub[a, b]: <:!< b = null }
and use case:
import extendedgenerictypes._ class foo def foobar[t](x: t)(implicit ev: t <:< foo) = "hello" def foobar[t](x: t)(implicit ev: t <:!< foo) = 5 println(foobar(new foo()))
unfortunately, results in ambiguity, , compiler not know of 2 methods invoke. i'm looking both explanation why there ambiguity in case (as opposed other simpler cases outlined in miles' gist) , how circumvent hurdle. note need perform check on parameter level (rather defining 1 method , doing check in body) because want have different return types.
the first problem because of way you're working in repl, second foobar
shadows first. if want overloaded definition, you'll need use :paste
define both @ once.
that still won't want, new error message:
scala> println(foobar(new foo)) <console>:14: error: ambiguous reference overloaded definition, both method foobar of type [t](x: t)(implicit ev: egt.<:!<[t,foo])int , method foobar of type [t](x: t)(implicit ev: <:<[t,foo])string match argument types (foo) , expected result type println(foobar(new foo)) ^
(note i've abbreviated extendedgenerictypes
because hate horizontal scrollbars.)
you can try providing <:<
instance explicitly:
scala> foobar(new foo)(implicitly[foo <:< foo]) <console>:14: error: ambiguous reference overloaded definition, both method foobar of type [t](x: t)(implicit ev: egt.<:!<[t,foo])int , method foobar of type [t](x: t)(implicit ev: <:<[t,foo])string match argument types (foo) foobar(new foo)(implicitly[foo <:< foo]) ^
so what's going on here compiler isn't going let second parameter list determine overloaded definition use. seems mean overloaded methods multiple parameter lists first parameter lists same useless. there's ticket this—at glance closest can come si-2383.
none of matters, though, because shouldn't using overloaded methods here—overloading terrible "feature" that's hangover java , breaks kinds of stuff.
there other possible approaches, though. of favorite weird scala tricks rely on fact can provide default value implicit parameter used if compiler can't find instance. if understand correctly, want this:
class foo def foobar[t](x: t)(implicit ev: t <:< foo = null) = option(ev).fold[either[int, string]](left(5))(_ => right("hello")) case class bar(i: int) extends foo case class baz(i: int)
and then:
scala> foobar(bar(13)) res0: either[int,string] = right(hello) scala> foobar(baz(13)) res1: either[int,string] = left(5)
note i'm using either
instead of letting existence of implicit determine return type. there ways accomplish (like shapeless's first-class polymorphic function values), they're overkill in case.
update: okay, since asked it:
import shapeless._ trait lowpriorityfoobar { this: poly1 => implicit def anyold[t] = at[t](_ => 5) } object foobar extends poly1 lowpriorityfoobar { implicit def foo[t](implicit ev: t <:< foo) = at[t](_ => "hello") }
and then:
scala> foobar(bar(13)) res6: string = hello scala> foobar(baz(13)) res7: int = 5
no wrapper. should think hard before taking approach, though.
update update, sake of completeness: can more directly (but more verbosely) without shapeless using dependent method types (again, you'll need use :paste
define these @ once):
class foo trait isfoomapper[i] { type out def apply(i: i): out } trait lowpriorityisfoomapper { implicit def isntfoo[a] = new isfoomapper[a] { type out = int def apply(a: a) = 5 } } object isfoomapper extends lowpriorityisfoomapper { implicit def isfoo[a](implicit ev: <:< foo) = new isfoomapper[a] { type out = string def apply(a: a) = "hello" } } def foobar[a](a: a)(implicit ifm: isfoomapper[a]) = ifm(a)
and then:
scala> foobar(bar(13)) res0: string = hello scala> foobar(baz(13)) res1: int = 5
again, advanced stuff , should used caution.
Comments
Post a Comment