Success/failure chain pattern in Scala - scala

I have a workflow like this:
parse template -> check consistency
-> check conformance of one template to another
parse template -> check consistency
Either one of those steps may fail. I would like to implement that in Scala, preferably so that the parallel branches get evaluated independently merging both their errors. Perhaps in a monadic style but I am curious about some general OOP pattern too. Currently I have multiple variations hardcoded for various actions with the chaining like this
def loadLeftTemplateAndForth (leftPath : String, rightPath : String) = {
val (template, errors) = loadTemplate(leftPath)
if(errors.isEmpty) loadRightTemplateAndForth(template, rightPath)
else popupMessage("Error.")
}
which I bet must be some kind of antipattern. The steps need decoupling from the workflow but I was not able to come up with anything extremely elegant and there must proven ways already.
EDIT:
Ok, so I have unsuccessfully tried to implement something like this
(((parseTemplate(path1) :: HNil).apply(checkConsistency _) :: ((parseTemplate(path2) :: HNil).apply(checkConsistency _)) :: HNil).apply(checkConformance _)
def checkConformance (t1 : Template)(t2 : Template) : Seq[Error]
The functions would then return Success(result) or Failure(errors). I was using HLists but got lost in the type inference rules and other issues. It seems I was pretty close though. For someone knowledgable of this stuff it would probably be a piece of cake.
EDIT:
I have finally managed to implement this
(parseTemplate("Suc") :: Args).apply(checkConsistency _) ::
(parseTemplate("Suc") :: Args).apply(checkConsistency _) :: Args)
.apply(checkConformance _)
with some unfornate constraints that each function must return my equivalent of Either and that the error type of applied function must be a subtype of arguments' error type. I did it using HList, application typeclass and a wrapper class Successful/UnsuccessfulArgList.

How about this?
// Allows conditional invocation of a method
class When[F](fun: F) {
def when(cond: F => Boolean)(tail: F => F) =
if (cond(fun)) tail(fun) else fun
}
implicit def whenever[F](fun: F): When[F] = new When[F](fun)
After that:
parseTemplate(t1).when(consistent _){
val parsed1 = _
parseTemplate(t2).when(consistent _){
conforms(parsed1, _)
}
}
Create some holder for errors, and pass it around (to parseTemplate, to consistent, to conforms), or use ThreadLocal.
Here is decoupled much more:
(parseTemplate(t1), parseTemplate(t2))
.when(t => consistent(t._1) && consistent(t._2)){ t =>
conforms(t._1, t._2)
}
EDIT
I've ended up with something like this:
def parse(path: String): Either[
String, // error
AnyRef // result
] = ?
def consistent(result: Either[String, AnyRef]): Either[
String, // error
AnyRef // result
] = ?
def conforms(result1: Either[String, AnyRef], result2: Either[String, AnyRef],
fullReport: List[Either[
List[String], // either list of errors
AnyRef // or result
]]): List[Either[List[String], AnyRef]] = ?
( (parse("t1") :: Nil).map(consistent _),
(parse("t2") :: Nil).map(consistent _)
).zipped.foldLeft(List[Either[List[String], AnyRef]]())((fullReport, t1t2) =>
conforms(t1t2._1, t1t2._2, fullReport))

Have your loadTemplate methods return Either[List[String], Template].
For errors return Left(List("error1",...)) and for success return Right(template).
Then you can do
type ELT = Either[List[String], Template]
def loadTemplate(path: String): ELT = ...
def loadRightTemplateAndForth(template: Template, rightPath: String): ELT = ...
def loadLeftTemplateAndForth(leftPath: String, rightPath: String): ELT =
for {
lt <- loadTemplate(leftPath).right
rt <- loadRightTemplateAndForth(lt, rightPath).right
} yield rt
The above is "fail fast", that is, it won't merge errors from the two branches. If the first fails it will return a Left and won't evaluate the second. See this project for code to handle error accumulation using Either.
Alternatively you can use Scalaz Validation. See Method parameters validation in Scala, with for comprehension and monads for a good explanation.

So the way I managed to do it is this (It still could use a refinements though - for example so that it constructs sequence of errors with type common to the list errors and function errors):
HList.scala
import HList.::
sealed trait HList [T <: HList[T]] {
def ::[H1](h : H1) : HCons[H1, T]
}
object HList {
type ::[H, T <: HList[T]] = HCons[H, T]
val HNil = new HNil{}
}
final case class HCons[H, T <: HList[T]](head: H, tail: T) extends HList[HCons[H, T]] {
override def ::[H1](h: H1) = HCons(h, this)
def apply[F, Out](fun : F)(implicit app : HApply[HCons[H, T], F, Out]) = app.apply(this, fun)
override def toString = head + " :: " + tail.toString
None
}
trait HNil extends HList[HNil] {
override def ::[H1](h: H1) = HCons(h, this)
override def toString = "HNil"
}
HListApplication.scala
#implicitNotFound("Could not find application for list ${L} with function ${F} and output ${Out}.")
trait HApply[L <: HList[L], -F, +Out] {
def apply(l: L, f: F): Out
}
object HApply {
import HList.::
implicit def happlyLast[H, Out] = new HApply[H :: HNil, H => Out, Out] {
def apply(l: H :: HNil, f: H => Out) = f(l.head)
}
implicit def happlyStep[H, T <: HList[T], FT, Out](implicit fct: HApply[T, FT, Out]) = new HApply[H :: T, H => FT, Out] {
def apply(l: H :: T, f: H => FT) = fct(l.tail, f(l.head))
}
}
ErrorProne.scala
sealed trait ErrorProne[+F, +S]
case class Success [+F, +S] (result : S) extends ErrorProne[F, S]
case class Failure [+F, +S] (errors : Seq[F]) extends ErrorProne[F, S]
ArgList.scala
import HList.::
import HList.HNil
sealed trait ArgList [E, L <: HList[L]] {
def apply[F, S](fun : F)(implicit app : HApply[L, F, ErrorProne[E, S]])
: ErrorProne[E, S]
def :: [A, E1 <: EX, EX >: E] (argument : ErrorProne[E1, A]) : ArgList[EX, A :: L]
}
case class SuccessArgList [E, L <: HList[L]] (list : L) extends ArgList[E, L] {
def apply[F, S](fun : F)(implicit app : HApply[L, F, ErrorProne[E, S]])
: ErrorProne[E, S] = app.apply(list, fun)
override def :: [A, E1 <: EX, EX >: E] (argument : ErrorProne[E1, A]) : ArgList[EX, A :: L] = argument match {
case Success(a) => SuccessArgList(a :: list)
case Failure(e) => FailureArgList(e)
}
}
case class FailureArgList [E, L <: HList[L]] (errors : Seq[E]) extends ArgList[E, L] {
def apply[F, S](fun : F)(implicit app : HApply[L, F, ErrorProne[E, S]])
: ErrorProne[E, S] = Failure(errors)
override def :: [A, E1 <: EX, EX >: E] (argument : ErrorProne[E1, A]) : ArgList[EX, A :: L] = argument match {
case Success(a) => FailureArgList(errors)
case Failure(newErrors) => FailureArgList(Seq[EX]() ++ errors ++ newErrors)
}
}
object Args {
def :: [E1, A] (argument : ErrorProne[E1, A]) : ArgList[E1, A :: HNil] = argument match {
case Success(a) => SuccessArgList(a :: HNil)
case Failure(e) => FailureArgList(e)
}
}
Usage
val result = ((parseTemplate("Suc") :: Args).apply(checkConsistency _) ::
(parseTemplate("Suc") :: Args).apply(checkConsistency _) :: Args)
.apply(checkConformance _)
trait Err
case class Err1 extends Err
case class Err2 extends Err
case class Err3 extends Err
def parseTemplate(name : String) : ErrorProne[Err, Int] = if(name == "Suc") Success(11) else Failure(Seq(Err1()))
def checkConsistency(value : Int) : ErrorProne[Err2, Double] = if(value > 10) Success(0.3) else Failure(Seq(Err2(), Err2()))
def checkConformance(left : Double) (right : Double) : ErrorProne[Err3, Boolean] =
if(left == right) Success(true) else Failure(Seq(Err3()))

Related

How to extract types from a tuple that implements a typeclass

Function a can receive single argument or a tuple, these arguments need to be members of typeclass StringIdentifiable
How to extract and decompose tuple type into types that also have instances of the typeclass
#typeclass trait StringIdentifiable[M] {
def identify(id: M): String
}
def a[K: StringIdentifiable] (k:K){
k match{
case (k1) =>
implicitly[StringIdentifiable[K]].identify(k1)
case (k1,k2) =>
implicitly[StringIdentifiable[k1.type]].identify(k1)
implicitly[StringIdentifiable[k2.type]].identify(k2)
}
I get error in the second match:
Could not find an instance of StringIdentifiable for k1.type
k1.type, k2.type are singleton types. Try
#typeclass trait StringIdentifiable[M] {
def identify(id: M): String
}
object StringIdentifiable {
implicit def one[K]: StringIdentifiable[K] = ???
implicit def two[K1: StringIdentifiable, K2: StringIdentifiable]: StringIdentifiable[(K1, K2)] = {
new StringIdentifiable[(K1, K2)] {
override def identify(id: (K1, K2)): String = id match {
case (k1,k2) =>
implicitly[StringIdentifiable[K1]].identify(k1)
implicitly[StringIdentifiable[K2]].identify(k2)
???
}
}
}
}
def a[K: StringIdentifiable](k:K): String = implicitly[StringIdentifiable[K]].identify(k)
You can do this with shapeless. For instance:
import shapeless._, ops.hlist._
object MyPoly extends Poly2 {
implicit def foo[A] = at[A, StringIdentifiable[A]]( (a, f) => f.identify(a) )
}
def a[K: StringIdentifiable, L <: HList, O <: HList](k: K)(
implicit
gen: Generic.Aux[K, L], // decompose K into HList L
lift: LiftAll.Aux[StringIdentifiable, L, O], // find an instance of StringIdentifiable for every element of L
zip: ZipWith[L, O, MyPoly.type] // zip L with its typeclass instances and map the results with the polymorphic function MyPoly
): String :: zip.Out = {
val l = gen.to(k)
val o = lift.instances
implicitly[StringIdentifiable[K]].identify(k) :: zip(l, o)
}
implicit def id1[A,B]: StringIdentifiable[(A, B)] = _ => "1"
implicit val id2: StringIdentifiable[String] = _ => "2"
implicit val id3: StringIdentifiable[Int] = _ => "3"
a(("foo", 42)) // 1 :: 2 :: 3 :: HNil
A full solution to your problem (IIUC) probably consists of using shapeless to automatically generate StringIdentifiable instances for all tuples.
trait StringIdentifiable[M] {
def identify(id: M): String
}
object StringIdentifiable {
object MyPoly extends Poly2 {
implicit def foo[A] = at[A, StringIdentifiable[A]]( (a, f) => f.identify(a) )
}
implicit def mkSI[K, L <: HList, O <: HList](
implicit
tup: IsTuple[K],
gen: Generic.Aux[K, L],
lift: LiftAll.Aux[StringIdentifiable, L, O],
zip: ZipWith[L, O, MyPoly.type]
): StringIdentifiable[K] = {
val o = lift.instances
k => {
val l = gen.to(k)
zip(l, o).mkString("(", ", ", ")")
}
}
}

scala: List found found : scala.collection.immutable.::[B] required: List[?U1] where type ?U1 <: U (this is a GADT skolem)

I am trying to implement List(the below code) and I am getting the error mentioned in the title.
From the error I understand that :: method should take type T but actually it is given as List[T]. Or I guess it is expecting to create an apply method.
object List {
def apply[T](x1: T, y1: T) = new Cons(x1, new Cons(y1, Nil))
def apply[T]() = Nil
def apply[T](x2: T) = new Cons(x2, Nil)
}
trait List[+T]{
def isEmpty : Boolean
def head : T
def tail : List[T]
def prepend [U >: T ] (elem : U) : List[U] = new Cons(elem, this)
override def toString = if (this.isEmpty) "." else "{" + head + tail + "}"
def isort [U >: T](xs: List[U]) : List[U] = xs match {
case Nil => Nil
case y :: ys => insert(y,isort(ys))
}
def :: [U >: T ] (elem: U) : List[U] = new Cons(elem, this)
def insert[U >: T](y: T, ys: List[T]) : List[T] = ys match {
case Nil => List(y)
case x :: xs => if (y <= x) y :: ys else x :: insert(y , xs)
}
}
class Cons[T](val head: T, val tail: List[T]) extends List[T]{
def isEmpty = false
}
object Nil extends List[Nothing]{
def isEmpty = true
def head: Nothing = throw new Error("head.List")
def tail: Nothing = throw new Error("tail.List")
}
Please suggest what parameter should the apply method have.
I've tried to repair it... It's too broken. I will therefore only enumerate the main error sources:
Case classes: Neither Nil nor Cons were case classes, why do you match on them?
Extractors: You have neither a case-class ::, nor any extractor called ::, so you can't pattern match on x :: xs. Use your own Cons(x, xs) instead
Ordering: You can't call <= on arbitrary elements, you need an Ordering somewhere in scope
Insert: The signature of insert didn't make any sense, it takes too many arguments.
Here is a compilable (but probably still very incorrect) version:
object List {
def apply[T](x1: T, y1: T) = new Cons(x1, new Cons(y1, Nil))
def apply[T]() = Nil
def apply[T](x2: T) = new Cons(x2, Nil)
}
import scala.math.Ordering
trait List[+T]{
def isEmpty : Boolean
def head : T
def tail : List[T]
def prepend [U >: T] (elem : U) : List[U] = new Cons(elem, this)
override def toString = if (this.isEmpty) "." else "{" + head + tail + "}"
def isort [U >: T](xs: List[U])(implicit ord: Ordering[U]): List[U] = xs match {
case Nil => Nil
case Cons(y, ys) => this.isort(ys).insert(y)
}
def :: [U >: T] (elem: U) : List[U] = new Cons(elem, this)
def insert[U >: T](y: U)(implicit ord: Ordering[U]): List[U] = {
import ord._
this match {
case Nil => List(y)
case Cons(x, xs) => if (y <= x) Cons(y, xs.insert[U](x)) else Cons(x, xs.insert(y))
}
}
}
case class Cons[T](val head: T, val tail: List[T]) extends List[T] {
def isEmpty = false
}
case object Nil extends List[Nothing] {
def isEmpty = true
def head: Nothing = throw new Error("head.List")
def tail: Nothing = throw new Error("tail.List")
}
I hope it contains enough hints so you can repair the rest.

Poly function from monomorphic functions that operate on coproduct

I'm working on an API that should enable to build a shapeless Poly1 function dynamically from standard monomorphic functions that operate on types of some coproduct.
The goal is to expose a simple method that receives a function as:
type FooCoproduct = Foo :+: Bar :+: CNil
def addF[E](f: E => E)(implicit ev: Inject[FooCoproduct, E]) = ???
and accumulate these functions in order to build a total Poly1 function covering all types in the coproduct. The evidence ev here is to force that the type paremeter E is a type in the coproduct.
After testing several approaches, including generic derivation of typeclasses, the most promising one has led me to accumulate these monomorphic functions in an HList and try to resolve the one that applies by means of a Selector. This is probably better understood by example:
object CoproductSample extends App {
import shapeless.{ :+:, CNil, Coproduct, HList, HNil, Poly1 }
import shapeless.ops.coproduct.Inject
import shapeless.ops.hlist.Selector
class Builder[A <: Coproduct] {
def accum[B](f: B => B, hl: HList)(implicit ev: Inject[A, B]) = f :: hl
class PolyBuilder[L <: HList](hl: L) extends Poly1 {
implicit def run[T](implicit ev: Selector[L, T => T]) =
at[T](hl.select[T => T])
}
}
type Cop = Int :+: String :+: CNil
val builder = new Builder[Cop]
val hl1 = builder.accum((i: Int) => i + 1, HNil)
val hl2 = builder.accum((s: String) => s + "one", hl1)
object pf extends builder.PolyBuilder(hl2)
val rInt = Coproduct[Cop](10).fold(pf)
val rStr = Coproduct[Cop]("ten").fold(pf)
}
This code doesn't compile with the message:
could not find implicit value for parameter folder:
shapeless.ops.coproduct.Folder[CoproductSample.pf.type, CoproductSample.Cop]
I suppose that I need to provide a Selector[L, T => T] where L is the type of the accumulated HList but I can't come up with the way to do this. On the other hand, I have the feeling that there must be a simpler solution to my problem.
Any help would be appreciated.
Update
After doing some more research I've come up with a solution that almost works. Unfortunately I'm not able to track the result type properly.
object CoproductSample {
import shapeless.{ CNil, Coproduct, HList, HNil, Inl, Inr, Poly2, ::, :+: }
// Accumulates ordinary functions from A => A in an HList
def accum[A, L <: HList](f: A => A, hl: L): (A => A) :: L = f :: hl
// A poly2 function that evaluates some monomorphic function present in
// an HList for certain value that satifies the signature of this function
object PolyEval extends Poly2 {
implicit def hnilCase[A]: Case.Aux[A, HNil, Option[A]] =
at[A, HNil]((a, l) => None)
implicit def hheadCaseSuccess[A, T <: HList]: Case.Aux[A, (A => A) :: T, Option[A]] =
at[A, (A => A) :: T]((a: A, l: (A => A) :: T) => Option(l.head(a)))
implicit def hheadCaseFail[A, H, T <: HList](
implicit tail: Case.Aux[A, T, Option[A]]
): Case.Aux[A, (H => H) :: T, Option[A]] =
at[A, (H => H) :: T]((a: A, l: (H => H) :: T) => PolyEval(a, l.tail))
}
// A poly2 function that uses `PolyEval` for evaluating a value present in
// a coproduct against an HList of monomorphic functions
object PolyEvalCop extends Poly2 {
implicit def cnilCase[A <: CNil, L <: HList]: Case.Aux[A, L, Option[A]] =
at[A, L]((a, l) => sys.error("Impossible!"))
implicit def cconsCase[H, T <: Coproduct, L <: HList](
implicit head: PolyEval.Case.Aux[H, L, Option[H]],
tail: Case[T, L]) // What is the return type here???)
= at[H :+: T, L]((c, l) =>
c match {
case Inl(h) => PolyEval(h, l)
case Inr(t) => PolyEvalCop(t, l)
})
}
}
Console session:
scala> import shapeless._, CoproductSample._
import shapeless._
import CoproductSample._
scala> case class Foo(i: Int); case class Bar(s: String)
defined class Foo
defined class Bar
scala> val f = (foo: Foo) => foo.copy(i = foo.i * 2)
f: Foo => Foo = <function1>
scala> val g = (bar: Bar) => bar.copy(s = bar.s + "_changed!")
g: Bar => Bar = <function1>
scala> val hl = accum(g, accum(f, HNil))
hl: shapeless.::[Bar => Bar,shapeless.::[Foo => Foo,shapeless.HNil.type]] = <function1> :: <function1> :: HNil
scala> type C = Foo :+: Bar :+: CNil
defined type alias C
scala> PolyEvalCop(Coproduct[C](Foo(10)), hl)
res1: Any = Some(Foo(20))
scala> PolyEvalCop(Coproduct[C](Bar("bar")), hl)
res2: Any = Some(Bar(bar_changed!))
The result type is not properly tracked and it's resolved as Any.
From the signature of addF it looks like you want to map over the Coproduct, i.e. modify it's value and stay on a coproduct, i.e. def add[E](e:E):E, if that were the case this would work:
# {
trait Add[E]{
def add(e:E):E
}
object Add{
def apply[E:Add]:Add[E] = implicitly[Add[E]]
implicit object cnil extends Add[CNil] {
def add(e:CNil) = throw new RuntimeException("Impossible")
}
implicit def coproduct[H, T <: Coproduct](
implicit
addH: Add[H],
addT:Add[T],
basis: ops.coproduct.Basis[H :+: T,T]
):Add[H :+: T] = new Add[H :+: T]{
def add(e: H :+: T) = e match {
case Inl(h) => Coproduct[H :+: T](addH.add(h)) // to stay in the Coproduct
case Inr(t) => addT.add(t).embed[H :+: T] // to stay in the coproduct
}
}
}
}
defined trait Add
defined object Add
# implicit def addString = new Add[String] {
def add(e:String) = e + "-ah"
}
defined function addString
# implicit def addInt = new Add[Int] {
def add(e:Int) = e + 1
}
defined function addInt
# type C = Int :+: String :+: CNil
defined type C
# val i = Coproduct[C](1)
i: C = 1
# Add[C].add(i)
res24: C = 2 // notice that the return type is C
# val s = Coproduct[C]("a")
s: C = a
# Add[C].add(s)
res26: C = a-ah // notice that the return type is C
It obviously works with "plain" types:
# Add[Int].add(1)
res38: Int = 2
The above is equivalent to map; but if you want a fold, i.e. def add[E](e:E):Int, you would just modify these two lines:
case Inl(h) => addH.add(h)
case Inr(t) => addT.add(t)

Clean up signatures with long implicit parameter lists

Is there an elegant solution to somehow clean up implicit parameter lists making signatures more concise?
I have code like this:
import shapeless._
import shapeless.HList._
import shapeless.ops.hlist._
import shapeless.poly._
trait T[I, O] extends (I => O)
trait Validator[P]
object Validator{
def apply[P] = new Validator[P]{}
}
object valid extends Poly1 {
implicit def caseFunction[In, Out] = at[T[In, Out]](f => Validator[In])
}
object isValid extends Poly2 {
implicit def caseFolder[Last, New] = at[Validator[Last], T[Last, New]]{(v, n) => Validator[New]}
}
object mkTask extends Poly1 {
implicit def caseT[In, Out] = at[T[In, Out]](x => x)
implicit def caseFunction[In, Out] = at[In => Out](f => T[In, Out](f))
}
object Pipeline {
def apply[H <: HList, Head, Res, MapRes <: HList](steps: H)
(implicit
mapper: Mapper.Aux[mkTask.type,H, MapRes],
isCons: IsHCons.Aux[MapRes, Head, _],
cse: Case.Aux[valid.type, Head :: HNil, Res],
folder: LeftFolder[MapRes, Res, isValid.type]
): MapRes = {
val wrapped = (steps map mkTask)
wrapped.foldLeft(valid(wrapped.head))(isValid)
wrapped
}
}
// just for sugar
def T[I, O](f: I => O) = new T[I, O] {
override def apply(v1: I): O = f(v1)
}
Pipeline(T((x:Int) => "a") :: T((x:String) => 5) :: HNil) // compiles OK
Pipeline(((x:Int) => "a") :: ((x:String) => 5) :: HNil) // compiles OK
// Pipeline("abc" :: "5" :: HNil) // doesn't compile
// can we show an error like "Parameters are not of shape ( _ => _ ) or T[_,_]"?
// Pipeline(T((x: Int) => "a") :: T((x: Long) => 4) :: HNil) // doesn't compile
// can we show an error like "Sequentiality constraint failed"?
And I also want to add a couple of implicit params necessary for the library's functionality (to the Pipeline.apply method), but the signature is already huge. I am worried about the ease of understanding for other developers - is there a "best practice" way to structure these params?
Edit: What I mean is the implicit parameters fall into different categories. In this example: mapper ensures proper content types, isCons, cse and folder ensure a sequential constraint on input, and I would like to add implicits representing "doability" of the business logic. How should they be grouped, is it possible to do in a readable format?
Edit2: Would it be possible to somehow alert the library's user, as to which constraint is violated? E.g. either the types in the HList are wrong, or the sequentiality constraint is not held, or he lacks the proper "business logic" implicits?
My suggestion was to use an implict case class that contains that configuration:
case class PipelineArgs(mapper: Mapper.Aux[mkTask.type,H, MapRes] = DEFAULTMAPPER,
isCons: IsHCons.Aux[MapRes, Head, _] = DEFAULTISCON,
cse: Case.Aux[valid.type, Head :: HNil, Res] = DEFAULTCSE,
folder: LeftFolder[MapRes, Res, isValid.type] = DEFAULTFOLDER) {
require (YOUR TESTING LOGIC, YOUR ERROR MESSAGE)
}
object Pipeline {
def apply[H <: HList, Head, Res, MapRes <: HList](steps: H)
(implicit args:PipelineArgs) = {
val wrapped = (steps map mkTask)
wrapped.foldLeft(valid(wrapped.head))(isValid)
wrapped
}
It doesn't help much w.r.t. clarity (but don't worry, I have seen worse), but it helps at notifying the user he's messing up at the creation of the args instance as you can a) put default values to the missing arguments in the CClass constructor b) put a number of "require" clauses.
Thanks to #Diego's answer, I have come up with the following code which works quite nicely:
import scala.annotation.implicitNotFound
import shapeless._
import shapeless.HList._
import shapeless.ops.hlist._
import shapeless.poly._
trait T[I, O] extends (I => O)
trait Validator[P]
object Validator{
def apply[P] = new Validator[P]{}
}
object valid extends Poly1 {
implicit def caseFunction[In, Out] = at[T[In, Out]](f => Validator[In])
}
object isValid extends Poly2 {
implicit def caseFolder[Last, New] = at[Validator[Last], T[Last, New]]{(v, n) => Validator[New]}
}
object mkTask extends Poly1 {
implicit def caseT[In, Out] = at[T[In, Out]](x => x)
implicit def caseFunction[In, Out] = at[In => Out](f => T[In, Out](f))
}
#implicitNotFound("Type constraint violated, elements must be of shape: (_ => _) or T[_, _]")
case class PipelineTypeConstraint[X, H <: HList, MapRes <: HList]
(
mapper: Mapper.Aux[X,H, MapRes]
)
implicit def mkPipelineTypeConstraint[X, H <: HList, MapRes <: HList]
(implicit mapper: Mapper.Aux[X,H, MapRes]) = PipelineTypeConstraint(mapper)
#implicitNotFound("Sequentiality violated, elements must follow: _[A, B] :: _[B, C] :: _[C, D] :: ... :: HNil")
case class PipelineSequentialityConstraint[Head, CRes, MapRes<: HList, ValidT, IsValidT]
(
isCons: IsHCons.Aux[MapRes, Head, _ <: HList],
cse: Case.Aux[ValidT, Head :: HNil, CRes],
folder: LeftFolder[MapRes, CRes, IsValidT]
)
implicit def mkPipelineSequentialityConstraint[Head, CRes, MapRes <: HList, ValidT, IsValidT]
(implicit isCons: IsHCons.Aux[MapRes, Head, _ <: HList],
cse: Case.Aux[ValidT, Head :: HNil, CRes],
folder: LeftFolder[MapRes, CRes, IsValidT]) = PipelineSequentialityConstraint(isCons, cse, folder)
object Pipeline {
def apply[H <: HList, Head, CRes, MapRes <: HList](steps: H)
(implicit
typeConstraint: PipelineTypeConstraint[mkTask.type, H, MapRes],
sequentialityConstraint: PipelineSequentialityConstraint[Head, CRes, MapRes, valid.type, isValid.type]
): MapRes = {
implicit val mapper = typeConstraint.mapper
implicit val isCons = sequentialityConstraint.isCons
implicit val cse = sequentialityConstraint.cse
implicit val folder = sequentialityConstraint.folder
val wrapped = (steps map mkTask)
wrapped.foldLeft(valid(wrapped.head))(isValid)
wrapped
}
}
// just for sugar
def T[I, O](f: I => O) = new T[I, O] {
override def apply(v1: I): O = f(v1)
}
Pipeline(T((x:Int) => "a") :: T((x:String) => 5) :: HNil) // compiles OK
Pipeline(((x:Int) => "a") :: ((x:String) => 5) :: HNil) // compiles OK
Pipeline(5 :: "abc" :: HNil)
// error = "Type constraint violated, elements must be of shape: (_ => _) or T[_, _]
Pipeline(T((x: Int) => "a") :: T((x: Long) => 4) :: HNil)
// error = "Sequentiality violated, elements must follow: (_[A, B] :: _[B, C] :: _[C, D] :: ... :: HNil"

Specifying or implementing a type class with variant types in Scala

I'm new to Scala and need some help for resolving a compilation error:
[error] .../traversals /traversals.scala:120: type mismatch;
[error] found : Traversable[Tree]
[error] required: Traversable[Node]
[error] Note: Tree >: Node, but trait Traversable is invariant in type T.
[error] You may wish to define T as -T instead. (SLS 4.5)
[error] println ("Sorted " + sorted (tree2) (monadApp,inOrder));
[error] ^
[error] one error found
I'm sorry that the MWE is that long, but I translated some type classes in a naive way from Haskell to Scala and stuck when I want to write some example that makes use of them.
I don't understand the problem exactly, but it seems as if either my Traversable trait does not allow that the T is replaced by some subtype or the concrete instance inOrder or the function sorted which uses the instance. As suggested by the compiler I tried to add some -s in front of the T at the Traversable trait, the Tree in front of the inOrder definition or the T in front of sorted, but it didn't help.
trait Applicative[M[_]] {
def pure[a] (x: a) : M[a]
def comp[a,b] (fx: M[a => b]) (mx: M[a]) : M[b]
def fmap[a,b] (fx: a => b) (mx: M[a]) : M[b]
= comp (pure (fx)) (mx)
}
trait Functor[F[_]] {
def fmap[a,b] (f: a => b) (m: F[a]) : F[b]
}
trait Traversable[T[_]] extends Functor[T] {
def dist[a,M[_]] (t: T[M[a]]) (implicit app : Applicative[M]) : M[T[a]]
def traverse[a,b,M[_]] (f: a => M[b]) (t : T[a]) (implicit app : Applicative[M]) : M[T[b]] =
dist (fmap(f) (t)) (app)
}
sealed abstract class Tree[a]
case class Empty[a] () extends Tree[a]
case class Node[a] (x : a, l : Tree[a], r: Tree[a]) extends Tree[a]
class TreeFunctor extends Functor[Tree] {
def fmap[a,b] (f: a => b) (t: Tree[a]) =
t match {
case Empty () => Empty ()
case Node (x, l, r) => Node (f (x), fmap (f) (l), fmap (f) (r))
}
}
trait Monoid[A] {
def mempty : A
def mappend (x: A) (y: A) : A
}
object BoolAnd extends Monoid[Boolean] {
def mempty = true
def mappend (x: Boolean) (y: Boolean) = x && y
}
case class K[b,a] (value: b)
class MonoidApplicative[m] (implicit monoid : Monoid[m]) extends Applicative[({type λ[α] = K[m,α]})#λ] {
def pure[a] (x : a) = K (monoid.mempty)
def comp[a,b] (f : K[m,a => b]) (x : K[m,a]) = K (monoid.mappend (f.value) (x.value))
}
case class State[s,a] (func: s => (a,s))
trait Monad[M[_]] {
def ret[a] (x : a) : M[a]
def bind[a,b] (mx : M[a]) (fx : a => M[b]) : M[b]
}
class StateMonad[S] extends Monad[({type λ[α] = State[S,α]})#λ] {
def ret[a] (x : a) = State ((s: S) => (x, s))
def bind[a,b] (mx : State[S,a]) (fx : a => State[S,b])
= State ((s : S) => (((tuple : (a,S)) => (fx (tuple._1)).func (tuple._2)) (mx.func (s))))
}
class MonadApp[M[_]] (implicit m : Monad[M]) extends Applicative[M] {
def pure[a] (x : a) = m.ret (x)
def comp[a,b] (fx : M[a => b]) (mx : M[a]) : M[b]
= m.bind[a => b,b] (fx) ((f : a => b) => m.bind[a,b] (mx) ((x:a) => m.ret (f (x))))
}
case class Comp[M[_],N[_],a] (unComp : M[N[a]])
class CompApp[M[_], N[_]] (implicit mapp : Applicative[M], napp: Applicative[N]) extends Applicative[({type λ[α] = Comp[M,N,α]})#λ] {
def pure[a] (x : a) = Comp (mapp.pure ( napp.pure ( x) ))
def comp[a,b] (mf : Comp[M,N,a => b]) (mx : Comp[M,N,a]) = Comp (mapp.comp (mapp.comp (mapp.pure (napp.comp[a,b]_)) (mf.unComp)) (mx.unComp) )
}
object Main {
implicit def inOrder : Traversable[Tree] = new Traversable[Tree]{
def dist[a,M[+_]] (t: Tree[M[a]]) (implicit app : Applicative[M])
= t match {
case Empty () => app.pure (Empty ())
case Node (x, l, r) => app.comp (app.comp (app.comp(app.pure ((l : Tree[a]) => ((x: a) => ((r: Tree[a]) => Node (x,l,r))))) (dist (l) (app))) (x)) (dist (r) (app))
}
}
val emptyTree = Empty[Int]()
val tree2 = Node(5, Node (2, Empty (), Empty ()), Node (9 , Empty (), Empty ()))
implicit def stateMonad[a] = new StateMonad[a] ()
implicit def monadApp = new MonadApp[({type λ[α] = State[Int,α]})#λ] () {}
implicit def andMonoidApp = new MonoidApplicative[Boolean] () (BoolAnd);
implicit def stateMonoidComp = new CompApp[({type κ[β] = State[Int,β]})#κ, ({type λ[α] = K[Boolean,α]})#λ ] () (monadApp, andMonoidApp)
def pairSort (x : Int) : State[Int,Boolean]
= State ((y : Int) => (y <= x, x))
def sorted[T[_]] (t : T[Int]) (implicit as : Applicative[({type λ[α] = State[Int,α]})#λ], tr : Traversable[T]) : Boolean
= (
(tr.traverse[Int,Boolean,({type λ[α] = Comp[({type κ[β] = State[Int,β]})#κ, ({type λ[α] = K[Boolean,α]})#λ , α]})#λ]
((i : Int) =>
Comp[({type κ[β] = State[Int,β]})#κ, ({type λ[α] = K[Boolean,α]})#λ , Boolean]
(as.fmap[Boolean, K[Boolean,Boolean]] ((x: Boolean) => K[Boolean, Boolean] (x)) (pairSort (i)))
)
(t)
//(CompApp[({type κ[β] = State[Int,β]})#κ, ({type λ[α] = K[Boolean,α]})#λ ])
(stateMonoidComp)
).unComp.func (-10000)
)._1.value
def main (args: Array[String])
= println ("Sorted " + sorted (tree2) (monadApp,inOrder));
}
The problem is that you have two different types: Traversable[Node] and Traversable[Tree]. This comes from the ADT translation from Haskell. Whereas in Haskell Node and Empty are both Tree, in Scala they are subtypes of Tree, which causes the concept of variance to come into play: given A subtype of B, is T[A] a subtype of T[B]?
If we look at the type of a function X => Y, we'll see it is declared Function1[-X, +Y], that is, it is contravariant in X and covariant in Y. That means a function that takes Tree and returns Node is a subtype of a function that takes Node and returns Tree. For example:
def f[T](n: Node[T])(func: Node[T] => Tree[T]): Tree = func(n)
def zeroRoot(tree: Tree[Int]): Node[Int] = Node(0, tree, Empty())
f(Node(1, Empty(), Empty())(zeroRoot)
The function zeroRoot works, because we are passing it a Node and it expected a Tree (which is ok), and we returned a Node, which in turn was returned as a Tree (also ok).
Incidentally, Tree should probably be co-variant.
Now, one other example of contra-variance is the ordering class. While Scala's invariant because of some technicalities, a proper ordering should be contra-variant. After all, if you know how to order a Tree, it follows that you can order a Node, so an Ordering[Tree] would be a subtype of Ordering[Node], since the first can be passed where the second is expected.
I haven't really paid close attention to the code, but it makes sense that something used as ordering ought to be contra-variant. On the other hand, it doesn't seem possible to make Traversable contravariant, since Functor type signature requires invariance.
Fix this problem by passing the type Tree explicitly to Sort, or declare tree2 explicitly as a Tree. Then you'll get other, different problems! :)