How do I use pattern matching with parametrized traits? - scala

I have trouble with Scala traits and type erasure. I have this trait:
trait Meta[T] {
def ~=(e: T): Boolean
}
Now I want to use pattern matching to check for this case:
(m,i) match {
case (x:Meta[T], y: T) if x ~= y => println ("right")
case _ => println ("wrong")}
The T from x: Meta[T] should be the type of y or y should be a subtype of T.
If the types don't match I get an ClassCastException. But x ~= y should not be executed if the types are not correct. Is there a away around this or do I have to catch the exception and handle it that way?
I made an running example as short as possible:
trait Meta[T] {
type t = T
def ~=(e: T): Boolean
}
sealed abstract class A
case class Ide(s: String) extends A
case class MIde(s: String) extends A with Meta[A] {
def ~=(e: A) = e match {
case e: Ide => true
case e: MIde => false
}
}
sealed abstract class B
case class Foo(s: String) extends B
object Test {
def m = MIde("x")
def i = Ide("i")
def f = Foo("f")
def main[T](args: Array[String]) {
(m, i) match {
case (x: Meta[T], y: T) if x ~= y => println("right")
case _ => println("wrong")
}
// -> right
(m, f) match {
case (x: Meta[T], y: T) if x ~= y => println("right")
case _ => println("wrong")
}
// -> Exception in thread "main" java.lang.ClassCastException:
// Examples.Foo cannot be cast to Examples.A
}
}

UPDATE: added alternative at the end.
You are experiencing the limitations of pattern matching when it comes to generic types, due to type erasure.
All is not lost however. We can rely on ClassManifests to implement a generic method to convert your classes to a target type T (and another similar on to convert to Meta[T]):
trait Meta[T] { this: T =>
type t = T
def metaManifest: ClassManifest[T]
def ~=(e: T): Boolean
}
abstract sealed class Base {
def as[T:ClassManifest]: Option[T] = {
if ( classManifest[T].erasure.isAssignableFrom( this.getClass ) ) Some( this.asInstanceOf[T] )
else None
}
def asMeta[T:ClassManifest]: Option[T with Meta[T]] = {
this match {
case meta: Meta[_] if classManifest[T] <:< meta.metaManifest => as[T].asInstanceOf[Option[T with Meta[T]]]
case _ => None
}
}
}
abstract sealed class A extends Base
case class Ide(s: String) extends A
case class MIde(s: String) extends A with Meta[A] {
val metaManifest = classManifest[A]
def ~=(e: A) = e match {
case e: Ide => true
case e: MIde => false
}
}
sealed abstract class B extends Base
case class Foo(s: String) extends B
Let's test this in the REPL:
scala> m.as[A]
res17: Option[A] = Some(MIde(x))
scala> m.asMeta[A]
res18: Option[A with Meta[A]] = Some(MIde(x))
scala> i.as[A]
res19: Option[A] = Some(Ide(i))
scala> i.asMeta[A]
res20: Option[A with Meta[A]] = None
scala> f.as[A]
res21: Option[A] = None
scala> f.asMeta[A]
res22: Option[A with Meta[A]] = None
Sounds good. Now we can rewrite our pattern matching from this:
(m, i) match {
case (x: Meta[T], y: T) if x ~= y => println("right")
case _ => println("wrong")
}
to this:
(m.asMeta[T], i.as[T]) match {
case (Some(x), Some(y)) if x ~= y => println("right")
case _ => println("wrong")
}
So your example would now look like this:
object Test {
val m = MIde("x")
val i = Ide("i")
val f = Foo("f")
def test[T:ClassManifest]() {
(m.asMeta[T], i.as[T]) match {
case (Some(x), Some(y)) if x ~= y => println("right")
case _ => println("wrong")
}
// -> right
(m.asMeta[T], f.as[T]) match {
case (Some(x), Some(y)) if x ~= y => println("right")
case _ => println("wrong")
}
}
}
UPDATE: if setting explictly metaManifest everytime you mix Meta is not an option, you can let scala automtically infer it by passing it implictly in Metas constructor. This means that Meta must now be a class, and as a consequence A and B (and all similar types that must appear as Meta's type parameter) must now be traits, as you can't mix 2 classes. So you are basically swapping a restriction for another one. Choose your favorite one.
Here we go:
abstract sealed class Meta[T]( implicit val metaManifest: ClassManifest[T] ) { this: T =>
type t = T
def ~=(e: T): Boolean
}
trait Base {
def as[T:ClassManifest]: Option[T] = {
if ( classManifest[T].erasure.isAssignableFrom( this.getClass ) ) Some( this.asInstanceOf[T] )
else None
}
def asMeta[T:ClassManifest]: Option[T with Meta[T]] = {
this match {
case meta: Meta[_] if classManifest[T] != ClassManifest.Nothing && classManifest[T] <:< meta.metaManifest => as[T].asInstanceOf[Option[T with Meta[T]]]
case _ => None
}
}
}
trait A extends Base
case class Ide(s: String) extends A
case class MIde(s: String) extends Meta[A] with A {
def ~=(e: A) = e match {
case e: Ide => true
case e: MIde => false
}
}
trait B extends Base
case class Foo(s: String) extends B
object Test {
val m = MIde("x")
val i = Ide("i")
val f = Foo("f")
def test[T:ClassManifest]() {
(m.asMeta[T], i.as[T]) match {
case (Some(x), Some(y)) if x ~= y => println("right")
case _ => println("wrong")
}
(m.asMeta[T], f.as[T]) match {
case (Some(x), Some(y)) if x ~= y => println("right")
case _ => println("wrong")
}
}
}
Finally, if neither solution suits you, you could try another one: instead of mixing Meta[T] with T, just wrap it. Meta[T] would then just be wrapper to T, and you could even add an implicit conversion from Meta[T] to its wrapped value, so that an instance of Meta[T] can effectively be used like an instance of T almost transparently.

Related

Shapeless type disjunction for more then 2 types

How to provide a type disjunction for 3 or more types in shapeless? Example:
import shapeless._
object Tst extends App {
sealed trait Base
final case class A() extends Base
final case class B() extends Base
final case class C() extends Base
final case class D() extends Base
def AorB[T: (A |∨| B)#λ](t: T): Unit =
t match {
case _: A => println("A")
case _: B => println("B")
}
AorB(A()) //Ok
AorB(B()) //Ok
def AorBorC[T: (A |∨| B |∨| C)#λ](t: T): Unit =
t match {
case _: A => println("A")
case _: B => println("B")
case _: C => println("C")
}
AorBorC(A()) //compile-error
AorBorC(B()) //compile-error
AorBorC(C()) //Ok
}
As can be seen for disjunction of 2 types it works completely fine. But for disjunction of 3 types it does not work as expected.
Compile errors are:
Error:(28, 10) Cannot prove that (Tst.A => Nothing) => Nothing <:< Object{type λ[X] = (X => Nothing) => Nothing <:< Tst.A => Nothing with Tst.B => Nothing => Nothing} => Nothing with Tst.C => Nothing => Nothing.
AorBorC(A())
and
Error:(29, 10) Cannot prove that (Tst.B => Nothing) => Nothing <:< Object{type λ[X] = (X => Nothing) => Nothing <:< Tst.A => Nothing with Tst.B => Nothing => Nothing} => Nothing with Tst.C => Nothing => Nothing.
AorBorC(B())
shapeless.|∨| doesn't work for more than 2 types.
http://milessabin.com/blog/2011/06/09/scala-union-types-curry-howard/
For more than 2 types encoding becomes more complicated.
One encoding is for 2, 4, 8 ... types
type ¬¬¬¬[T] = ¬¬[¬¬[T]]
type |∨∨|[T, U] = {
type λ[X] = ¬¬¬¬[X] <:< (T ∨ U)
}
def AorBorC[T: ((A ∨ B) |∨∨| (C ∨ C))#λ](t: T): Unit =
t match {
case _: A => println("A")
case _: B => println("B")
case _: C => println("C")
}
AorBorC(A()) //Ok
AorBorC(B()) //Ok
AorBorC(C()) //Ok
Another is for arbitrary number of types
trait Disj[T] {
type or[S] = Disj[T with ¬[S]]
type apply = ¬[T]
}
type ∨∨∨[T1, T2, T3] = Disj[¬[T1]]#or[T2]#or[T3]#apply
type |∨∨∨|[T1, T2, T3] = {
type λ[X] = ¬¬[X] <:< ∨∨∨[T1, T2, T3]
}
def AorBorC[T: |∨∨∨|[A, B, C]#λ](t: T): Unit =
t match {
case _: A => println("A")
case _: B => println("B")
case _: C => println("C")
}
AorBorC(A()) //Ok
AorBorC(B()) //Ok
AorBorC(C()) //Ok
How to define "type disjunction" (union types)?

Phantom existential types in Scala

I'm trying to write a value class that wraps Scala collection's Map and provides an alternative get. I'm trying to use a phantom type in the value class and tag the Key with the same type using the method member. If the result of member is Some(k) then the user should be able to call get(k) and get a V instead of an Option[V].
import scala.collection.{Map => M}
class Key[PH, K] private (val k: K) extends AnyVal
object Key {
def apply[PH, K](k: K): Key[PH, K] = new Key(k)
}
class Map[PH, K, V] private (val m: M[K, V]) extends AnyVal {
def member(k: K): Option[Key[PH, K]] = m.get(k).map(_ => Key(k))
def get(key: Key[PH, K]): V = m.get(key.k).get
}
object Map {
def apply[PH, K, V](m: M[K, V]): Map[PH, K, V] = new Map(m)
}
def withMap[K, V, T](m: M[K, V])(cont: (Map[PH, K, V] forSome { type PH }) => T): T = cont(Map(m))
withMap(M("a" -> "a")){ m =>
m.member("a") match {
case Some(v) => println(m.get(v))
case None => println(":(")
}
}
But currently it fails compiling with the following error:
found : Key[PH(in value $anonfun),String] where type +PH(in value $anonfun)
required: Key[PH(in value cont),String]
case Some(v) => println(m.get(v))
How can I convince scalac that the PHs are the same?
Destructure the existential:
withMap(M("a" -> "a")) { case m =>
m.member("a") match {
case Some(v) => println(m.get(v))
case None => println(":(")
}
}
This abbreviates
withMap(M("a" -> "a")) { case m: Map[ph, String, String] => // name the phantom type ph, name the map m =>
m.member("a") match {
case Some(v) => println(m.get(v))
case None => println(":(")
}
}
The destructuring allows m to be given a non-existential type in terms of a newly introduced type variable. This means that every occurrence of m can now have the same type.
Phantom types are meaningless. You should say what you mean: every Key belongs to a certain Map:
import scala.collection.immutable.Map // it is not safe to use Maps in general!
class KeyedMap[K, V](val m: Map[K, V]) extends AnyVal {
import KeyedMap._
def member(k: K): Option[Key[K, V, m.type]] = m.get(k).map { _ => new Key[K, V, m.type](k) }
def fromKey(k: Key[K, V, m.type]): V = m(k.k)
}
object KeyedMap {
// vvvvvvvvvvvvvv requires this parameter to be <something>.type
class Key[K, +V, M <: Map[K, V] with Singleton] private[KeyedMap](val k: K) extends AnyVal
}
object Test {
def main(args: String*): Unit = {
val m = new KeyedMap(Map("a" -> "b"))
m.member("a") match {
case Some(v) => println(m.fromKey(v))
case None => println(":(")
}
}
}

When using HList with GADTs I am having to cast using asInstanceOf[H]. Is there a way to avoid the cast?

Given 2 GADT Algebras which know about each other and 2 interpreters that are mutually recursive, I am having issues having to cast from type A to type h <: HList even though in the context of the pattern match, it should be implied that type A is type h.
Is there a way to avoid the asInstanceOf[h] call in the interpreter?
abstract class KvpHList[H<:HList]
object KvpNil extends KvpHList[HNil]
case class KvpCons[H <: A :: T,A, T<:HList](head: KvpValue[A], tail: KvpHList[T])(implicit isHCons: IsHCons.Aux[H,A,T]) extends KvpHList[H] {
val hCons: IsHCons.Aux[H,A,T] = isHCons
}
abstract class KvpValue[A]
case object StringData extends KvpValue[String]
case class HListData[H <:HList](member: KvpHList[H]) extends KvpValue[H]
def hListInterpreter[H<:HList](hList: KvpHList[H]): H => String = {
hList match {
case KvpNil => (hNil: H) => "Nil"
case cons: KvpCons[H,a,t]=> {
implicit val hCons = cons.hCons
(input: H) => {
s"${kvpInterpreter(cons.head)(input.head)} :: ${hListInterpreter(cons.tail)(input.tail)}"
}
}
}
}
def kvpInterpreter[A](kvpValue: KvpValue[A]): A => String = {
kvpValue match {
case StringData => (str: String) => str
case h: HListData[h] => {
(input: A) => { // tried (input: h) as well
val toString: h => String = hListInterpreter(h.member)
toString(input.asInstanceOf[h]) // <--- CASTING :(
}
}
}
}
kvpInterpreter(HListData(KvpCons(StringData, KvpNil))).apply("Hello" :: HNil)
Since H in KvpCons is uniquely determined by A and T, KvpCons can be parametrized with two type parameters rather than three.
Type-level pattern matching is type classes
abstract class KvpHList[H <: HList]
object KvpNil extends KvpHList[HNil]
case class KvpCons[A, T <: HList](head: KvpValue[A], tail: KvpHList[T]) extends KvpHList[A :: T]
abstract class KvpValue[A]
case object StringData extends KvpValue[String]
case class HListData[H <: HList](member: KvpHList[H]) extends KvpValue[H]
trait HListInterpreter[H <: HList] {
def apply(hList: KvpHList[H]): H => String
}
object HListInterpreter {
implicit val nil: HListInterpreter[HNil] = new HListInterpreter[HNil] {
override def apply(hList: KvpHList[HNil]): HNil => String = _ => "Nil"
}
implicit def cons[A, T <: HList](implicit
headKvpInterpreter: KvpInterpreter[A],
tailHListInterpreter: HListInterpreter[T]
): HListInterpreter[A :: T] = new HListInterpreter[A :: T] {
override def apply(hList: KvpHList[A :: T]): A :: T => String = hList match {
case cons: KvpCons[_, _] => input => s"${headKvpInterpreter(cons.head)(input.head)} :: ${tailHListInterpreter(cons.tail)(input.tail)}"
}
}
}
def hListInterpreter[H <: HList](hList: KvpHList[H])(implicit hListInterp: HListInterpreter[H]): H => String = hListInterp(hList)
trait KvpInterpreter[A] {
def apply(kvpValue: KvpValue[A]): A => String
}
object KvpInterpreter {
implicit val string: KvpInterpreter[String] = new KvpInterpreter[String] {
override def apply(kvpValue: KvpValue[String]): String => String = str => str
}
implicit def hList[H <: HList : HListInterpreter]: KvpInterpreter[H] = new KvpInterpreter[H] {
override def apply(kvpValue: KvpValue[H]): H => String = kvpValue match {
case h: HListData[H] => input => {
val toString: H => String = hListInterpreter(h.member)
toString(input)
}
}
}
}
def kvpInterpreter[A](kvpValue: KvpValue[A])(a: A)(implicit kvpInterp: KvpInterpreter[A]): String = kvpInterp(kvpValue)(a)

Getting around invariant result type in State

I would like to define a State that builds a concrete subtype of a trait, as per decodeFoo:
sealed trait Foo
case class Bar(s: String) extends Foo
case class Baz(i: Int) extends Foo
val int: State[Seq[Byte], Int] = State[Seq[Byte], Int] {
case bs if bs.length >= 4 =>
bs.drop(4) -> ByteBuffer.wrap(bs.take(4).toArray).getInt
case _ => sys.error(s"Insufficient data remains to parse int")
}
def bytes(len: Int): State[Seq[Byte], Seq[Byte]] = State[Seq[Byte], Seq[Byte]] {
case bs if bs.length >= len => bs.drop(len) -> bs.take(len)
case _ => sys.error(s"Insufficient data remains to parse $len bytes")
}
val bytes: State[Seq[Byte], Seq[Byte]] = for {
len <- int
bs <- bytes(len)
} yield bs
val string: State[Seq[Byte], String] = bytes.map(_.toArray).map(new String(_, Charset.forName("UTF-8")))
val decodeBar: State[Seq[Byte], Bar] = string.map(Bar)
val decodeBaz: State[Seq[Byte], Baz] = int.map(Baz)
val decodeFoo: State[Seq[Byte], Foo] = int.flatMap {
case 0 => decodeBar
case 1 => decodeBaz
}
This will not compile as State is defined in cats as type State[S, A] and the compiler responds:
Error:(36, 15) type mismatch;
found : cats.data.State[Seq[Byte],FooBarBaz.this.Bar]
(which expands to) cats.data.IndexedStateT[cats.Eval,Seq[Byte],Seq[Byte],FooBarBaz.this.Bar]
required: cats.data.IndexedStateT[cats.Eval,Seq[Byte],Seq[Byte],FooBarBaz.this.Foo]
Note: FooBarBaz.this.Bar <: FooBarBaz.this.Foo, but class IndexedStateT is invariant in type A.
You may wish to define A as +A instead. (SLS 4.5)
case 0 => decodeBar
I can work around this by widening the definitions of decodeBar & decodeBaz to be of type State[Seq[Byte], Foo]. Is that the best way forward? Or can I take a different approach that avoids widening these types?
Functor.widen
Functor.widen should do the trick. Full compilable example (with kind-projector):
import cats.data.State
import cats.Functor
object FunctorWidenExample {
locally {
sealed trait A
case class B() extends A
val s: State[Unit, B] = State.pure(new B())
val t: State[Unit, A] = Functor[State[Unit, ?]].widen[B, A](s)
}
}
in your case, it would be something like:
val decodeFoo: State[Seq[Byte], Foo] = int.flatMap {
case 0 => Functor[State[Seq[Byte], ?]].widen[Bar, Foo](decodeBar)
case 1 => Functor[State[Seq[Byte], ?]].widen[Bar, Foo](decodeBaz)
}
Other possible work-arounds
(not really necessary, just to demonstrate the syntax that might be less known):
Explicit type ascriptions:
val decodeFoo: State[Seq[Byte], Foo] = int.flatMap {
case 0 => decodeBar.map(x => (x: Foo))
case 1 => decodeBaz.map(x => (x: Foo))
}
Using <:< as method (those things actually do have a meaningful apply):
val decodeFoo: State[Seq[Byte], Foo] = int.flatMap {
case 0 => decodeBar.map(implicitly: Bar <:< Foo)
case 1 => decodeBaz.map(implicitly: Baz <:< Foo)
}

Type class implementation for hierarchy

I am studying Scala and trying to implement some abstractions for custom types. Defining scalaz monoids for concrete classes is quite straightforward. But how to declare one Monoid for the type hierarchy? Assuming this code:
sealed trait Base
case class A(v:Int) extends Base
object N extends Base
object Main {
// Wanna one monoid for all the Base's
implicit val baseMonoid = new Monoid[Base] {
override def append(f1: Base, f2: => Base): Base = f1 match {
case A(x) => f2 match {
case A(y) => A(x + y)
case N => A(x)
}
case N => f2
}
override def zero = N
}
def main(args: Array[String]): Unit = {
println(∅[Base] |+| A(3) |+| A(2)) // Compiles
println(A(3) |+| A(2)) // Not compiles
}
}
How to make state A() |+| B() workable in the example above?
This compiles:
import scalaz._, Scalaz._
sealed trait Base
case class A(a: Int) extends Base
case class B(b: Int) extends Base
object N extends Base
object BullShit {
// Wanna one monoid for all the Base's
implicit val sg: Semigroup[Base] = new Semigroup[Base] {
override def append(f1: Base, f2: => Base): Base = f1 match {
case A(a) => f2 match {
case A(a1) => A(a + a1)
case B(b) => A(a + b)
case N => N
}
case B(b) => f2 match {
case A(a) => B(a + b)
case B(b1) => B(b + b1)
case N => N
}
case N => f2
}
}
println((A(1): Base) |+| (B(2): Base))
}
And your example would compile if you tell Scala's horrible type inferencer what you mean:
sealed trait Base
case class A(v: Int) extends Base
object N extends Base
object Main {
// Wanna one monoid for all the Base's
implicit val baseMonoid = new Monoid[Base] {
override def append(f1: Base, f2: => Base): Base = f1 match {
case A(x) => f2 match {
case A(y) => A(x + y)
case N => A(x)
}
case N => f2
}
override def zero = N
}
def main(args: Array[String]): Unit = {
import scalaz._, Scalaz._
println(∅[Base] |+| A(3) |+| A(2)) // Compiles
println((A(3): Base) |+| (A(2): Base)) // now it compiles
}
}