Last active
December 30, 2015 20:39
-
-
Save squito/7882131 to your computer and use it in GitHub Desktop.
I first thought of making a union bound using implicit classes, but I realized it would need extra object allocation. The better answer is found here:
http://stackoverflow.com/a/3508555/1442961 HOWEVER, that answer doesn't work for varargs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| trait Wrapper { | |
| def v: String | |
| } | |
| implicit class StringAsWrapper(s: String) extends Wrapper { | |
| println(s"constructing string wrapper") | |
| def v = s | |
| } | |
| implicit class SymbolAsWrapper(s: Symbol) extends Wrapper { | |
| println(s"constructing symbol as wrapper") | |
| def v = s.name | |
| } | |
| def foo(w: Wrapper) = w.v | |
| foo("hi") | |
| foo("bye") | |
| foo("blah") | |
| foo('wakka) | |
| foo('ooga) | |
| foo('booga) | |
| //if you run this, you'll see you are generating the implicit wrapper on each call | |
| trait WrapperVal extends Any { | |
| def v: String | |
| } | |
| implicit class StringAsWrapperVal(val s: String) extends AnyVal with WrapperVal { | |
| def v = s | |
| } | |
| implicit class SymbolAsWrapperVal(val s: Symbol) extends AnyVal with WrapperVal { | |
| def v = s.name | |
| } | |
| def blah(w:WrapperVal) = w | |
| //I can't modify this example to show this, b/c you can't add any other statements in | |
| // the the constructor of a value class. I think I'm getting multiple instances | |
| // based on this rule: | |
| // | |
| // http://docs.scala-lang.org/overviews/core/value-classes.html | |
| // A value class is actually instantiated when: | |
| // 1. a value class is treated as another type. | |
| // ... | |
| // I'm not sure how, but this comparison returns true | |
| blah("foo") == blah("foo") | |
| //but if you look at the returned value, you can see we're allocating actual objects | |
| scala> blah("foo") | |
| res21: WrapperVal = StringAsWrapperVal@18cc6 | |
| scala> blah("ooga") | |
| res22: WrapperVal = StringAsWrapperVal@3422ba | |
| // the Witness version works, though, w/out any extra allocations | |
| trait UnionBound[T] | |
| object UnionBound { | |
| //these are just objects, so nothing could possibly be getting allocated | |
| implicit object StringWitness extends UnionBound[String] | |
| implicit object SymbolWitness extends UnionBound[Symbol] | |
| } | |
| def ooga[T: UnionBound] (t: T) = t match { | |
| case str: String => str | |
| case sym: Symbol => sym.name | |
| } | |
| ooga("blah") | |
| ooga("foo") | |
| ooga('wakka) | |
| ooga(1)//compiler error, our union bound works! | |
| // BUT! unfortunately, we get into trouble if we try to use varargs | |
| //wrapper version works: | |
| def foos(ws: Wrapper*) = ws.map{_.v} | |
| scala> foos("blah", 'hi, 'there, "everybody") | |
| constructing string wrapper | |
| constructing symbol as wrapper | |
| constructing symbol as wrapper | |
| constructing string wrapper | |
| res12: Seq[String] = ArrayBuffer(blah, hi, there, everybody) | |
| //but "witness" does not | |
| def oogas[T: UnionBound](ts: T*) = ts.map{t => t match { | |
| case str: String => str | |
| case sym: Symbol => sym.name | |
| }} | |
| scala> oogas("hi", "there") | |
| res14: Seq[String] = ArrayBuffer(hi, there) | |
| scala> oogas('symbol, 'ok) | |
| res16: Seq[String] = ArrayBuffer(symbol, ok) | |
| scala> oogas("mixed", 'fails) | |
| <console>:13: error: could not find implicit value for evidence parameter of type UnionBound[java.io.Serializable] | |
| oogas("mixed", 'fails) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment