Skip to content

Instantly share code, notes, and snippets.

@logicalguess
Last active September 21, 2017 01:57
Show Gist options
  • Save logicalguess/a104fffad4c0213363d562a85e85d198 to your computer and use it in GitHub Desktop.
Save logicalguess/a104fffad4c0213363d562a85e85d198 to your computer and use it in GitHub Desktop.
type safe partial function union/merge with compile time verification
package logicalguess.receiver
import scala.reflect.ClassTag
case class Call[U <: Union](pf: PartialFunction[Any, Any]) {
import Call._
def union[In](f: Function[In, Any])(implicit ct: ClassTag[In]) = Call[U | In](pf.orElse(downcast(f)))
def apply[In](in: In)(implicit ev: In isPartOf U) = pf(in)
}
object Call {
def empty =
Call[Nothing | Nothing](emptyFunction)
def union[A](f: PartialFunction[A, Any])(implicit ct: ClassTag[A]) =
Call[Nothing | A](emptyFunction.orElse(downcast(f)))
def downcast[In](f: In => Any)(implicit ct: ClassTag[In]): PartialFunction[Any, Any] = {
case (in: In) => f(in)
}
}
object emptyFunction extends PartialFunction[Any, Any] {
def isDefinedAt(x: Any) = false
def apply(x: Any) = throw new UnsupportedOperationException("Empty function")
}
package logicalguess.receiver
import scala.reflect.ClassTag
trait CompositeReceiver extends Receiver[Any] {
type O = Any
trait TypeHolder {
type U <: Union
val call: Call[U]
}
var holder: TypeHolder = new TypeHolder {
override type U = Nothing | Nothing
override val call: Call[|[Nothing, Nothing]] = Call.empty
}
//var pf: PartialFunction[Any, Any] = emptyFunction
def receiver[I: ClassTag](next: PartialFunction[I, Any]) {
val h = holder
holder = new TypeHolder {
type U = h.U | I
val call = h.call.union[I](next)
}
//pf = pf.orElse(Call.downcast(next))
}
def receive(in: Any): Any = holder.call.pf(in) //can't use call directly...
}
object Example {
val pfs: PartialFunction[String, Any] = {
case s: String => s + s
}
val pfi: PartialFunction[Int, Any] = {
case i: Int => i*i
}
def main(args: Array[String]): Unit = {
trait StringReceiver { this: CompositeReceiver =>
receiver { pfs }
}
trait IntReceiver { this: CompositeReceiver =>
receiver { pfi }
}
object receiver extends CompositeReceiver with StringReceiver with IntReceiver
println(receiver.receive("abc"))
println(receiver.receive(7))
}
}
package logicalguess.receiver
import scala.reflect.ClassTag
import scala.language.implicitConversions
trait Receiver[I] {
type O
def receive(in: I): O
}
object Receiver {
def from[In, Out](pf: PartialFunction[In, Out]): Receiver[In] = new Receiver[In] {
type O = Out
override def receive(in: In): Out = pf(in)
}
implicit def receiver2function[In](r: Receiver[In])(implicit ct: ClassTag[In]): PartialFunction[In, Any] = {
case in: In => r.receive(in)
}
}
package logicalguess.receiver
/**
* http://blog.knutwalker.de/typed-actors/tut/union.html
*/
import scala.annotation.implicitNotFound
sealed trait Union
sealed trait |[+A, +B] extends Union
@implicitNotFound("Cannot prove that ${A} is a union type.")
sealed trait IsUnion[-A] {
type Out <: Union
}
object IsUnion {
type Aux[-A0, U0 <: Union] = IsUnion[A0] {type Out = U0}
implicit def isUnion[A <: Union]: Aux[A, A] =
new IsUnion[A] {
type Out = A
}
}
@implicitNotFound("Cannot prove that message of type ${A} is a member of ${U}.")
sealed trait isPartOf[A, +U <: Union]
object isPartOf extends IsPartOf0 {
implicit def leftPart[A](implicit ev: A isNotA Union): isPartOf[A, A | Nothing] =
null
implicit def rightPart[A](implicit ev: A isNotA Union): isPartOf[A, Nothing | A] =
null
}
sealed trait IsPartOf0 {
implicit def tailPart1[A, U <: Union](implicit partOfTl: A isPartOf U): isPartOf[A, U | Nothing] =
null
implicit def tailPart2[A, U <: Union](implicit partOfTl: A isPartOf U): isPartOf[A, Nothing | U] =
null
}
// @annotation.implicitAmbiguous("${A} must not be <: ${B}")
sealed trait isNotA[A, B]
object isNotA {
implicit def nsub[A, B]: A isNotA B = null
// $COVERAGE-OFF$Code only exists to prove non-equality and is expected to never execute
implicit def nsubAmbig1[A, B >: A]: A isNotA B = sys.error("Unexpected invocation")
implicit def nsubAmbig2[A, B >: A]: A isNotA B = sys.error("Unexpected invocation")
// $COVERAGE-ON$
}
package logicalguess.receiver
object UnionExample {
sealed trait InputA
case class IntA(val i: Int) extends InputA
case class BoolA(val b: Boolean) extends InputA
sealed trait InputB
case class StringB(val s: String) extends InputB
case class IntB(val i: Int) extends InputB
case class Bool(val b: Boolean)
val pfa: PartialFunction[InputA, Any] = {
case IntA(i) => i
case BoolA(b) => b
}
val pfb: PartialFunction[InputB, Any] = {
case StringB(s) => s
case IntB(i) => i
}
val pfc: PartialFunction[Bool, Any] = {
case Bool(b) => b
}
import Call._
val pf = downcast(pfa).orElse(downcast(pfb))
val ra: Receiver[InputA] = Receiver.from(pfa)
val rb: Receiver[InputB] = Receiver.from(pfb)
val rc: Receiver[Bool] = Receiver.from(pfc)
def main(args: Array[String]): Unit = {
println("function call IntA: " + pf(IntA(55)))
println("function call BoolA: " + pf(BoolA(true)))
println("function call StringB: " + pf(StringB("abc")))
println("function call IntB: " + pf(IntB(77)))
//println(pf(Bool(true))) // runtime error: MatchError
type Input = InputA | InputB
//val call = Call[Input](pf)
val call = Call.empty.union(pfa).union(pfb)
println("evidence call IntA: " + call(IntA(55)))
println("evidence call BoolA: " + call(BoolA(true)))
println("evidence call StringB: " + call(StringB("abc")))
println("evidence call IntB: " + call(IntB(77)))
//call(Bool(true)) //compile error
//type Inputs = |[|[InputA, InputB], Bool]
//println(Call[Input | Bool](downcast(pfa).orElse(downcast(pfb)).orElse(downcast(pfc)))(Bool(false)))
val chain: Call[|[|[|[Nothing, InputA], InputB], Bool]] = Call.union(pfa).union(pfb).union(pfc)
println(chain(Bool(false)))
println(chain(IntA(99)))
println(chain(StringB("xyz")))
val rchain = Call.empty.union(ra).union(rb).union(rc)
println(rchain(Bool(false)))
println(rchain(IntA(99)))
println(rchain(StringB("xyz")))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment