-
Notifications
You must be signed in to change notification settings - Fork 50
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support additional type args in @free and @tagless algebras #247
Comments
Here is a simple example in which I am seeing the problems: @free trait Ann[A]{
def bob(i: Int): FS[A]
def karl(a: A): FS[Int]
} The macro expands this trait Ann[FF1[_], A] extends freestyle.EffectLike[FF1] {
def bob(i: Int): FS[A]
def karl(a: A): FS[Int]
}
object Ann {
sealed abstract trait Op[AA1]
case class BobOP[A](i: Int) extends Op[A]
case class KarlOP[A](a: A) extends Op[Int]
abstract trait Handler[MM1[_], A] extends FunctionK[Op, MM1] {
protected[this] def bob(i: Int): MM1[A]
protected[this] def karl(a: A): MM1[Int]
override def apply[AA1](fa1: Op[AA1]): MM1[AA1] = fa1 match {
case (l @ BobOP(_)) => bob(l.i)
case (l @ KarlOP(_)) => karl(l.a)
}
}
class To[LL1[_], A](ii1: Inject[Op, LL1]) extends Ann[LL1, A] {
private[this] val toInj1 = FreeS.inject[Op, LL1](ii1)
override def bob(i: Int): FS[A] = toInj1(BobOP(i))
override def karl(a: A): FS[Int] = toInj1(KarlOP(a))
}
implicit def to[LL1[_], A](implicit ii1: Inject[Op, LL1]): To[LL1, A] = new To[LL1, A]()
def apply[LL1[_], A](implicit ev1: Ann[LL1, A]): Ann[LL1, A] = ev1
} The generated Scala code gives the following two errors:
|
Fiddling with the example program above, I have found so far two directions, that could allow us to put the type parameters back:
|
With the trait Ann[FF1[_]] extends freestyle.EffectLike[FF1] {
def bob[A](i: Int): FS[A]
def karl[A](a: A): FS[Int]
}
object Ann {
sealed abstract trait Op[AA1]
case class BobOP[A](i: Int) extends Op[A]
case class KarlOP[A](a: A) extends Op[Int]
trait Handler[MM1[_]] extends FunctionK[Op, MM1] {
protected[this] def bob[A](i: Int): MM1[A]
protected[this] def karl[A](a: A): MM1[Int]
override def apply[AA1](fa1: Op[AA1]): MM1[AA1] = fa1 match {
case (l @ BobOP(_) ) => bob(l.i)
case (l @ KarlOP(_)) => karl(l.a)
}
}
class To[LL1[_]](implicit ii1: Inject[Op, LL1]) extends Ann[LL1] {
private[this] val toInj1 = FreeS.inject[Op, LL1](ii1)
override def bob[A](i: Int): FS[A] = toInj1(BobOP(i))
override def karl[A](a: A): FS[Int] = toInj1(KarlOP(a))
}
implicit def to[LL1[_]](implicit ii1: Inject[Op, LL1]): To[LL1] = new To[LL1]()
def apply[LL1[_], A](implicit ev1: Ann[LL1]): Ann[LL1] = ev1
} |
The trait Ann[FF1[_], A] extends freestyle.EffectLike[FF1] {
def bob(i: Int): FS[A]
def karl(a: A): FS[Int]
}
class AnnProvider[A] {
sealed abstract trait Op[AA1]
case class BobOP(i: Int) extends Op[A]
case class KarlOP(a: A) extends Op[Int]
trait Handler[MM1[_]] extends FunctionK[Op, MM1] {
protected[this] def bob(i: Int): MM1[A]
protected[this] def karl(a: A): MM1[Int]
override def apply[AA1](fa1: Op[AA1]): MM1[AA1] = fa1 match {
case (l @ BobOP(_) ) => bob(l.i)
case (l @ KarlOP(_)) => karl(l.a)
}
}
class To[LL1[_]](implicit ii1: Inject[Op, LL1]) extends Ann[LL1, A] {
private[this] val toInj1 = FreeS.inject[Op, LL1](ii1)
override def bob(i: Int): FS[A] = toInj1(BobOP(i))
override def karl(a: A): FS[Int] = toInj1(KarlOP(a))
}
implicit def to[LL1[_]](implicit ii1: Inject[Op, LL1]): To[LL1] = new To[LL1]()
def apply[LL1[_]](implicit ev1: Ann[LL1, A]): Ann[LL1, A] = ev1
}
object Ann {
def apply[A] = new AnnProvider[A]
} |
@raulraja ¿Have you tried before any of these alternatives? ¿Do you have any preference? The first option may have the problem that, since each method now becomes parametrised, it may be necessary to add type parameters to every method call. Another possible problem is that the Handler and the To classes have each a separate type parameter, so they may not be aligned. The second option may have the problem of introducing this new class. Also, the syntax for using parametrised type-classes would not be too different from the current one. As in the current effects, we would still need to write something like val p = Ann[Double]
def program: p[p.Op] Which is to say, we would still need to use a middle declaration for accessing the |
I think both approaches affect so much to the final syntax and affect the user experience that we should settle this for now until we have a better way to deal with this problem. The current workaround is to define a wrapper class as in the state and reader effects and that is a much more sane approach for users than having to pass type args on each call or referring to explicit instances for |
Update: in a recent PR and issue, we now support type parameters in the |
Currently no type args are supported in @free and @tagless algebras forcing to define Algebras nested in dependent types such as in the
reader
orstate
effects in theeffects
module. We should remove this limitation if possible to allow any number of arbitrary type args beside the implicitF[_]
that they all have.The text was updated successfully, but these errors were encountered: