Matching Type Parameters on Traits - scala

Let's say I have a trait like:
trait MyTrait[T, U <: SomeParentClass] {
def get(data: T): Option[U]
}
and a concrete implementation like:
case class MyStringClass[U <: SomeParentClass](f: String => Option[U])
extends MyTrait[String, U] {
override def get(data: String) = f(data)
}
To simplify things, let's also say we have the following types for U <: SomeParentClass:
TypeA
TypeB
TypeC
and some functions:
def str2TypeA(s: String): Option[TypeA] = ...
def str2TypeB(s: String): Option[TypeB] = ...
def str2TypeC(s: String): Option[TypeC] = ...
Then let's say I have:
val mySeq = Seq(
MyStringClass(str2TypeA),
MyStringClass(str2TypeB),
MyStringClass(str2TypeC)
)
What I want to do is filter mySeq based on the return type U. Something like:
mySeq.collect { case a: Function1[_, Option[TypeA]] => a}
I'm running into type erasure issues as expected. I'm curious what approaches might work well here to achieve my goal of filtering based on the type U.

You would usually use a TypeTag to handle cases where erasure gets in your way. They are essentially simple objects that contain all of the information available at compile-time, and they're easy to obtain either by requesting an implicit parameter, or just writing typeTag[T]. For example:
import scala.reflect.runtime.universe._
case class MyStringClass[U <: SomeParentClass](f: String => Option[U])(implicit val utt: TypeTag[U])
extends MyTrait[String, U] {
override def get(data: String) = f(data)
}
mySeq.collect {
case a: MyStringClass[Option[TypeA]] if (a.utt == typeTag[Option[Type[A]]) => a
}

Related

How to override Scala generic methods for specific types?

I currently have something like this:
case class Bear(a: String, b: String) {
val can: Can[T] = ??
def drink[T](str: String) = can.open(str)
}
I need to modify this to be used for only 4 types A,B,C and D. For example given an instance of Bear we should only be able to call bearinstance.drink[A]("abc"), bearinstance.drink[B]("abc"), bearinstance.drink[C]("abc") and bearinstance.drink[D]("abc"). Any other type should not be allowed.
Now the question is how do I rewrite this method for specific types?
Another issue is with the can, assuming I manage to rewrite drink to be used with only types 'A', 'B', 'C' and 'D', I will have to create can for all the four types as member variables. How do I make generic method to dynamically select the can based on the type? One option is to implicitly declare can outside the class, but it requires class parameters to be declared.
Any leads will be appreciated.
The fact that you need to do this means you really should refactor your code.
But anyways...
Try using implicit parameters:
case class Bear(a: String, b: String) {
val can: Can[T] = ???
def drink[T](str: String)(implicit ev: CanDrink[T]) = can.open(str)
}
Then make a trait CanDrink with implicit instances:
trait CanDrink[T]
implicit object ACanDrink extends CanDrink[A]
implicit object BCanDrink extends CanDrink[B]
//And so on
And now you can call it like this:
bearinstance.drink[A]("abc")
//Gets implicit object ACanDrink
bearinstance.drink[X]("abc")
//Doesn't work because no implicit parameter given of type CanDrink[X]
In Dotty, you could try changing the definition of drink using union types, as suggested by Dmytro Mitin:
def drink(x: A | B | C | D)(str: String) = ???
def drink[T](str: String)(using T <:< (A | B | C | D)) = ???
If you need it to be determined dynamically, use ClassTag.
def drink[T](str: String)(implicit ev: ClassTag[T]) = ev match {
case classOf[A] => ???
...
}
If you're actually overriding generic method you must implement it for all possible types of type parameters (otherwise you violate the contract of class):
trait BearLike {
def drink[T](str: String)
}
case class Bear(a: String, b: String) extends BearLike {
override def drink[T](str: String) = ??? // for all T
}
or
case class Bear(a: String, b: String) {
def drinkABCD[T](str: String)(implicit can: Can[T]) = can.open(str) // only for A, ..., D
}
or
case class Bear(a: String, b: String) extends BearLike {
override def drink[T](str: String): Unit = sys.error("Bear is not actually a BearLike")
def drinkABCD[T](str: String)(implicit can: Can[T]) = can.open(str) // only for A, ..., D
}
provided there are Can[A], ..., Can[D]
trait Can[T] {
def open(str: String)
}
object Can {
implicit val a: Can[A] = ???
implicit val b: Can[B] = ???
implicit val c: Can[C] = ???
implicit val d: Can[D] = ???
}
If you can modify the contract then you can add this restriction (that the method works only for A, ..., D) to the contract
trait BearLike {
def drink[T](str: String)(implicit can: Can[T])
}
case class Bear(a: String, b: String) extends BearLike {
override def drink[T](str: String)(implicit can: Can[T]) = can.open(str)
}
Sometimes it's not easy to combine FP (type classes) with OOP (inheritance). Normally if you start to work with type classes (Can) you should prefer type classes further on
trait BearLike[B] {
def drink(str: String)
}
case class Bear[T](a: String, b: String)
object Bear {
implicit def bearIsBearLike[T](implicit can: Can[T]): BearLike[Bear[T]] = new BearLike[Bear[T]] {
override def drink(str: String): Unit = can.open(str)
}
}
How to define "type disjunction" (union types)?

Elegant way to chain Scala partial functions

I am looking for an elegant way to chain partial functions that derive from a common base type. The idea is that each partial function handles a type so they become easy to compose for different types and have a common catch-all if the chained partial function is undefined:
trait Message
trait SysMessage extends Message
trait UserMessage extends Message
case class TextSysMessage(m: String) extends SysMessage
case class TextUserMessage(m: String) extends UserMessage
class Test {
type MessagePF = scala.PartialFunction[Message, String]
type SysMessagePF = scala.PartialFunction[SysMessage, String]
type UserMessagePF = scala.PartialFunction[UserMessage, String]
def getSysMessage: SysMessagePF = {
case sm: TextSysMessage ⇒ s"System message: ${sm.m}"
}
def getUserMessage: UserMessagePF = {
case um: TextUserMessage ⇒ s"User message: ${um.m}"
}
def * : MessagePF = {
case m ⇒ s"Unknown message: $m"
}
// Chained partials fails because `m` is a SysMessage with UserMessage
def handler(m: Message): String = (getSysMessage orElse getUserMessage orElse *)(m)
}
Clearly, this approach does not compile. I can get around this by nested pattern matching like this
def getSysMessage: MessagePF = {
case m: SysMessage ⇒ m match {
case sm: TextSysMessage ⇒ s"System message: ${sm.m}"
}
}
but then I loose the capability of handling unknown messages in a catch all. Is there some elegant way to achieve this goal?
As addition to #adamwy + Hongxu Chen's answer, you can define your very own combinator, which involves implicit parameters, so enforces slightly different application syntax
implicit class PartFuncOps[A: ClassTag, B](pf: PartialFunction[A, B]) {
def or[D >: A, C <: D : ClassTag](other: PartialFunction[C, B]): PartialFunction[D, B] = {
case a: A if pf.isDefinedAt(a) ⇒ pf(a)
case c: C if other.isDefinedAt(c) ⇒ other(c)
}
}
Now you can write
def combine = getSysMessage or getUserMessage or *
def handler(m: Message): String = combine(m)
Or
def handler(m: Message): String = (getSysMessage or getUserMessage or *).apply(m)
As suggested by #adamwy, you can change the partial function type to be:
type MessagePF = scala.PartialFunction[Message, String]
type SysMessagePF = scala.PartialFunction[Message, String]
type UserMessagePF = scala.PartialFunction[Message, String]

Scala Mutually Convertible Generic Types

I'm very new to Scala programming, and I really like the degree to which code is composable. I wanted to write some traits that deal with two related objects that are convertible to each other, and build more functionality by continuing to extend that trait so that when I create objects I can specify the related types for my generics. Here is a working toy example of the type of code I'm talking about:
trait FirstConverter[First] {
def toFirst: First
}
trait SecondConverter[Second] {
def toSecond: Second
}
trait TwoWayConverter[First <: SecondConverter[Second], Second <: FirstConverter[First]] {
def firstToSecond(x: First) = x.toSecond
def secondToFirst(x: Second) = x.toFirst
}
trait RoundTripConverter[First <: SecondConverter[Second], Second <: FirstConverter[First]] extends TwoWayConverter[First, Second] {
def firstToFirst(x: First) = secondToFirst(firstToSecond(x))
def secondToSecond(x: Second) = firstToSecond(secondToFirst(x))
}
case class A(s: String) extends SecondConverter[B] {
def toSecond: B = B((s.toInt) + 1)
}
case class B(i: Int) extends FirstConverter[A] {
def toFirst: A = A((i * 2).toString)
}
object ABConverter extends RoundTripConverter[A, B]
object Main {
def main(args: Array[String]): Unit = {
println(ABConverter firstToSecond A("10")) // 11
println(ABConverter secondToFirst B(42)) // 84
println(ABConverter firstToFirst A("1")) // 4
println(ABConverter secondToSecond B(2)) // 5
}
}
While this works, I'm not sure if it's idiomatic Scala. I'm asking if there are any tricks to make the type definitions more concise and if I can somehow define the type restrictions only once and have them used by multiple traits which extend other traits.
Thanks in advance!
One way to improve your design would be to use a type class instead of inheriting from FirstConverter and SecondConverter. That way you could use multiple conversion functions for the same types and convert between classes you don't control yourself.
One way would be to create a type class which can convert an A into a B :
trait Converter[A, B] {
def convert(a: A): B
}
trait TwoWayConverter[A, B] {
def firstToSecond(a: A)(implicit conv: Converter[A, B]): B = conv.convert(a)
def secondToFirst(b: B)(implicit conv: Converter[B, A]): A = conv.convert(b)
}
trait RoundTripConverter[A, B] extends TwoWayConverter[A, B] {
def firstToFirst(a: A)(implicit convAB: Converter[A, B], convBA: Converter[B, A]) =
secondToFirst(firstToSecond(a))
def secondToSecond(b: B)(implicit convAB: Converter[A, B], convBA: Converter[B, A]) =
firstToSecond(secondToFirst(b))
}
We could create type class instances for the following two classes Foo and Bar similar to your A and B
case class Foo(s: String)
case class Bar(i: Int)
implicit val convFooBarFoor = new Converter[Foo, Bar] {
def convert(foo: Foo) = Bar((foo.s toInt) + 1)
}
implicit val convBarFoo = new Converter[Bar, Foo] {
def convert(bar: Bar) = Foo((bar.i * 2) toString)
}
We then could create a FooBarConverter :
object FooBarConverter extends RoundTripConverter[Foo, Bar]
FooBarConverter firstToSecond Foo("10") // Bar(11)
FooBarConverter secondToFirst Bar(42) // Foo(84)
FooBarConverter firstToFirst Foo("1") // Foo(4)
FooBarConverter secondToSecond Bar(2) // Bar(5)
The only problem is because we can not pass parameters to a trait, we can not limit the types to types with a Converter type class instance. So you can create the StringIntConverter below even if no Converter[String, Int] and/or Convert[Int, String] instances exist.
object StringIntConverter extends TwoWayConverter[String, Int]
You cannot call StringIntConverter.firstToSecond("a") because the firstToSecond method needs the implicit evidence of the two mentioned type class instances.

How to remove repetitive case statements for implementations of a sealed trait

I often find that I need to extract the type of a sealed trait before doing the same thing to each implementation:
sealed trait Trait
case class Foo() extends Trait
case class Bar() extends Trait
// ... lots of other implementations
// *must* take a `Trait`, not a `T <: Trait`
def thing(t: Trait): ??? = t match {
case f: Foo => // something with the instance and specific type
case b: Bar => // something with the instance and specific type
// ... same thing again for other implementations
}
for example
// typically provided by somebody else...
trait Thing[T] { def thingy: String }
implicit def thing[T]: Thing[T] = new Thing[T] { def thingy = "stuff" }
def thing(t: Trait): String = t match {
case Foo() => implicitly[Thing[Foo]].thingy
case Bar() => implicitly[Thing[Bar]].thingy
// ...
}
I'd like to reduce the boilerplate involved in doing this.
UPDATE: nowadays we'd use typeclass derivation via shapeless. e.g. https://github.com/fommil/shapeless-for-mortals
It turns out that you can use shapeless' polymorphic functions and co-product to do this:
object thing extends Poly1 {
implicit def action[T <: Trait: Thing] = at[T](
a => implicitly[Thing[T]].thingy
)
// magic that makes it work at the sealed trait level
def apply(t: Trait): String =
Generic[Trait].to(t).map(thing).unify
}
which can then be used like
println(thing(Foo(): Trait))
I'd like to make this easier to write via an abstract class (let's forget about passing on implicit parameters to action for now), e.g.
abstract class MatchSealed[In, Out] extends Poly1 {
implicit def poly[T <: In] = at[T](action)
def action[T <: In](t: T): Out
import ops.coproduct.Mapper
def apply[R <: HList](in: In)(
implicit
g: Generic.Aux[In, R],
m: Mapper[this.type, R]
): Out = {
val self: this.type = this
g.to(in).map(self).unify
}
}
but this is failing with a missing Mapper[self.type, g.Repr] on the final line. I don't know which implicit is missing, but I suspect it is the self.type. I really want to capture realisedSelf.type but I don't know how to do that.
UPDATE: it turns out that it is not possible to obtain the Mapper because it needs access to the realised object Unable to map on HList

Pattern matching on abstract types with a fold

This is a follow-up to a previous question I asked which was far from being complete. All the code that follows compiles and run fine in the Scala console.
Consider the following abstract data type, along with the operations it must support as a typeclass:
trait SIG {
type XOrY
type X <: XOrY
type Y <: XOrY
}
trait SIGOps[Sig <: SIG] {
def makeX(s: String): Sig#X
def makeY(i: Int): Sig#Y
// the disjunction is enforced with that fold
def fold[T](xy: Sig#XOrY)(isX: String => T, isY: Int => T): T
// the following is for convenience, as we want to mimick case classes
object X {
def apply(s: String): Sig#X = makeX(s)
def unapply(xy: Sig#XOrY): Option[String] = fold(xy)(s => Some(s), i => None)
}
object Y {
def apply(i: Int): Sig#Y = makeY(i)
def unapply(xy: Sig#XOrY): Option[Int] = fold(xy)(s => None, i => Some(i))
}
}
And now, here is a possible implementation for the signature. The typeclass instance is in the companion object for easy discovery.
trait EitherSig extends SIG {
type XOrY = scala.util.Either[String, Int]
type X = scala.util.Left[String, Int]
type Y = scala.util.Right[String, Int]
}
object EitherSig {
implicit object EitherSIGOps extends SIGOps[EitherSig] {
def makeX(s: String): EitherSig#X = scala.util.Left[String, Int](s)
def makeY(i: Int): EitherSig#Y = scala.util.Right[String, Int](i)
def fold[T](xy: EitherSig#XOrY)(isX: String => T, isY: Int => T): T = xy match {
case Left(s) => isX(s)
case Right(s) => isY(s)
}
}
}
And finally, here is how one can write code that depends on the abstract signature.
class Example[Sig <: SIG](implicit ops: SIGOps[Sig]) {
import ops._
def main(args: Array[String]): Unit = {
val xy: Sig#XOrY = X("foo")
xy match {
case X(s) => println("X: "+s)
// Scala does not see that the pattern matching is not exhaustive if when I comment the following line
// case Y(i) => println("Y: "+i)
}
}
}
object ConcreteExample extends Example[EitherSig]
And it works as expected:
scala> ConcreteExample.main(Array())
X: foo
The question is the following: how can I teach Scala to recognize when the pattern matching is not exhaustive as above?
There might be a way to communicate this information to the typechecker but I don't know how.
Jason Zaugg gave an answer on Twitter:
[...] we don't have an extension point for that. Only sealed hierarchies are considered.
Travis Brown proposed to Write a matchExhaustive macro that inspects the partial function but doesn't think that it's Worth the trouble when you've got the fold.
So no real solution for that question for now.