Elegant way to chain Scala partial functions - scala

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]

Related

Using same type parameter as argument type and parameter type with match expression

I get errors by compiling following example code.
abstract class Base
case class A(i: Int) extends Base
case class B(s: String) extends Base
class Transform {
def func[T <: Base](arg: T): T = arg match {
case A(i) => A(i)
case B(s) => B(s)
}
}
errors are
Example.scala:9: error: type mismatch;
found : A
required: T
case A(i) => A(i)
^
Example.scala:10: error: type mismatch;
found : B
required: T
case B(s) => B(s)
^
two errors found
These errors are reasonable.
To avoid this, I need to put asInstanceOf[T] behind instantiation like A(i).asInstanceOf[T]. However, it is annoying to do like that for all return value if there are a lot of match case patterns.
In addition, I want to use Transform class as parent class and override func() to execute specific operation like below code.
class ExtTransform extends Transform {
override def func[T <: Base](arg: T): T = arg match {
case A(i) => A(i + 1)
case _ => super.func(arg)
}
}
Are there better ways or some trick?
To avoid this, I need to put asInstanceOf[T] behind instantiation like A(i).asInstanceOf[T]. However, it is annoying to do like that for all return value if there are a lot of match case patterns.
Well, that problem is an easy one: put it in one place at the end of the match instead of every branch.
override def func[T <: Base](arg: T): T = (arg match {
case A(i) => A(i)
case B(s) => B(s)
}).asInstanceOf[T]
But please note your design is inherently unsafe because there are subtypes of Base other than Base, A, and B: singleton types (a.type), compound types (A with SomeTrait), Null... and any of them can be used as T. It may be better just to have overloads:
class Transform {
def func(arg: Base): Base = arg match {
case arg: A => func(arg)
case arg: B => func(arg)
}
def func(arg: A): A = arg
def func(arg: B): B = arg
}
class ExtTransform extends Transform {
override def func(arg: A): A = A(arg.i + 1)
}
I would suggest using a typeclass instead.
sealed trait Base
object Base {
final case class A() extends Base
final case class B() extends Base
sealed trait Builder[T <: Base] {
def build(): T
}
object Builder {
final implicit val ABuilder: Builder[A] = new Builder[A] {
override def build(): A = A()
}
final implicit val BBuilder: Builder[B] = new Builder[B] {
override def build(): B = B()
}
}
}
object Main extends App {
def func[T <: Base](implicit builder: Base.Builder[T]): T =
builder.build()
func[Base.A] // res: Base.A = A()
func[Base.B] // res: Base.B = B()
}

Abstract type, variables and typeclasses in Scala

I'm trying to make a typeclass that depends on user input. Imagine we have some case objects:
sealed trait H
case object Ha extends H
case object Hb extends H
and the type class:
trait Foo[A] {
def bar: String
}
object Foo {
def bar[A : Foo] = implicitly[Foo[A]].bar
implicit object FooA extends Foo[Ha.type] {
override def bar: String = "A"
}
implicit object FooB extends Foo[Hb.type] {
override def bar: String = "B"
}
}
While I found a working solution using a match:
variableComingFromMainArgs match {
case "a" => Foo.bar[Ha.type] _
case "b" => Foo.bar[Hb.type] _
}
I remember that we have abstract types in Scala, so I could change my case class into:
sealed trait H {
type T <: H
}
case object Ha extends H {
type T = this.type
}
case object Hb extends H {
type T = this.type
}
Now, when depending on user input to the program, I could do something like
val variable = Ha
println(Foo.bar[variable.T])
However, for some reason this doesn't work the and the error is not very useful for me:
error: could not find implicit value for evidence parameter of type Foo[variable.T]
println(Foo.bar[variable.T])
Any ideas if this can be overcome, if not, why?
Thanks.
Implicits are compile time constructs so in principle they cannot depend on user input directly (programmer can wire it for example with pattern matching as you did).
Consider the following code. It compiles and works as intended:
trait H {
type A
}
case object Ha extends H {
override type A = Int
}
case object Hb extends H {
override type A = Long
}
trait Adder[T] {
def add(a: T, b: T): T
}
implicit object IntAdder extends Adder[Int] {
override def add(a: Int, b: Int): Int = a + b
}
implicit object LongAdder extends Adder[Long] {
override def add(a: Long, b: Long): Long = a + b
}
def addWithAdder(input: H)(a: input.A, b: input.A)(implicit ev: Adder[input.A]): input.A = ev.add(a, b)
val x: Int = addWithAdder(Ha)(3, 4)
val y: Long = addWithAdder(Hb)(3, 4)
Let's focus on addWithAdder method. Thanks to path dependent types compiler can choose correct implicit for this task. But still this method is basically the same as the following:
def add[T](a: T, b: T)(implicit ev: Adder[T]) = ev.add(a, b)
The only advantage first one can have is that you can provide all instances yourself and stop the user of your code to add own types (when H is sealed and all implementations are final).

Matching Type Parameters on Traits

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
}

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.

How to return correct type from generic function passed a related abstract type parameter

I am trying to write "better" (more idiomatic?) Scala code for the following circumstance:
I have a set of classes that will be identified by a reference field that belongs to a parallel set of reference case classes, something like the following:
abstract sealed class Ref(value: String)
case class ARef(value: String) extends Ref(value)
case class BRef(value: String) extends Ref(value)
case class CRef(value: String) extends Ref(value)
trait Referenced {
type refType <: Ref
val ref: refType
}
trait A extends Referenced { type refType = ARef }
trait B extends Referenced { type refType = BRef }
trait C extends Referenced { type refType = CRef }
Another class (which will probably turn into the state type of a State monad) will contain lists of these types, and provide a function to retrieve an object, given its reference. I want this returned value to be appropriately typed, i.e. given
val aRef = ARef("my A ref")
I want to be able to make a call like:
val myA: Option[A] = context.get[A](aRef)
and be sure to get back an Option[A], not just an Option[Referenced]. My best attempt to achieve this so far looks something like the following:
trait Context {
// ... other stuff ...
protected val aList: List[A]
protected val bList: List[B]
protected val cList: List[C]
def get[R <: Referenced](ref: R#refType): Option[R] = {
val result = ref match {
case aRef: ARef => aList.find(_.ref == aRef)
case bRef: BRef => bList.find(_.ref == bRef)
case cRef: CRef => cList.find(_.ref == cRef)
case _ => throw new RuntimeException("Unknown Ref type for retrieval: "+ref)
}
result.asInstanceOf[Option[R]]
}
}
which seems to work correctly, but has that smelly "asInstanceOf" call in it. I would be interested in seeing ideas on how this might be done better (and check that I haven't just missed an obvious simpler solution).
Note that for other reasons, I have thus far elected to go with abstract typing rather than parameter types (trait A extends Referenced[ARef] style), but could change this if the reasons were compelling enough.
The machinery needed to do this without casting really isn't all that heavy in this case ... it's just another example of a functional dependency.
In what follows we rely on the fact that type Ref is sealed so that we can simply enumerate the alternatives. Your Ref and Reference hierarchies remain unchanged, and we add a relation type Rel to both express the type-level correspondence between the two, and to make an appropriate value-level selection,
trait Rel[Ref, T] {
def lookup(as: List[A], bs: List[B], cs: List[C])(ref: Ref) : Option[T]
}
object Rel {
implicit val relA = new Rel[ARef, A] {
def lookup(as: List[A], bs: List[B], cs: List[C])(ref: ARef) : Option[A] =
as.find(_.ref == ref)
}
implicit val relB = new Rel[BRef, B] {
def lookup(as: List[A], bs: List[B], cs: List[C])(ref: BRef) : Option[B] =
bs.find(_.ref == ref)
}
implicit val relC = new Rel[CRef, C] {
def lookup(as: List[A], bs: List[B], cs: List[C])(ref: CRef) : Option[C] =
cs.find(_.ref == ref)
}
}
Now we can reimplement Context without pattern matches or casts as follows,
trait Context {
// ... other stuff ...
protected val aList: List[A] = ???
protected val bList: List[B] = ???
protected val cList: List[C] = ???
def get[R <: Ref, T](ref: R)(implicit rel: Rel[R, T]): Option[T] =
rel.lookup(aList, bList, cList)(ref)
}
And we can use this new definition like so,
object Test {
def typed[T](t: => T) {} // For pedagogic purposes only
val context = new Context {}
val aRef = ARef("my A ref")
val myA = context.get(aRef)
typed[Option[A]](myA) // Optional: verify inferred type of myA
val bRef = BRef("my B ref")
val myB = context.get(bRef)
typed[Option[B]](myB) // Optional: verify inferred type of myB
val cRef = CRef("my C ref")
val myC = context.get(cRef)
typed[Option[C]](myC) // Optional: verify inferred type of myC
}
Notice that the resolution of the implicit Rel argument to get computes the type of the corresponding Reference from the type of the ref argument, so we're able to avoid having to use any explicit type arguments at get's call-sites.
I am just going to reiterate my own (current) "answer" for my question, because I thought it would be interesting/instructive to allow readers to vote it up or down to generate a more direct comparison with answers provided by others.
trait Context {
// ... other stuff ...
protected val aList: List[A]
protected val bList: List[B]
protected val cList: List[C]
def get[R <: Referenced](ref: R#refType): Option[R] = {
val result = ref match {
case aRef: ARef => aList.find(_.ref == aRef)
case bRef: BRef => bList.find(_.ref == bRef)
case cRef: CRef => cList.find(_.ref == cRef)
case _ => throw new RuntimeException("Unknown Ref type for retrieval: "+ref)
}
result.asInstanceOf[Option[R]]
}
}