Last active
December 14, 2016 15:09
-
-
Save b-studios/024ce3ff0e262e6eedeac30fb0c2d179 to your computer and use it in GitHub Desktop.
New encoding for extensible objects
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
scalaVersion := "2.11.7" | |
// For this dependency you have to clone | |
// https://github.com/b-studios/MixinComposition | |
// and run `sbt publishLocal` | |
libraryDependencies += "de.unimarburg" % "mixin-composition_2.11" % "0.2-SNAPSHOT" | |
libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value | |
libraryDependencies += "org.scalaz" %% "scalaz-core" % "7.0.6" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package obj.extend | |
import de.unimarburg.composition | |
import de.unimarburg.composition.WithF | |
object functors { | |
// reexport type of functor | |
type Functor[F[_]] = scalaz.Functor[F] | |
// The top element of the lattice | |
type AnyF[+_] = AnyRef | |
def AnyF[S] = (s: S) => new AnyF[S] {} | |
// Allows spawning a functor instance for the intersection of `C` and `E` | |
def merge[C[+_], E[+_]](implicit cf: Functor[C], ef: Functor[E], mix: C WithF E) = | |
new Functor[(C WithF E)#Apply] { | |
type F[X] = (C WithF E)#Apply[X] | |
def map[A, B](fa: F[A])(f: A => B): F[B] = mix(cf.map(fa)(f), ef.map(fa)(f)) | |
} | |
// Converting de.unimarburg.composition.Functor to scalaz.Functor: | |
implicit def functorConv[F[_]](implicit functor: composition.Functor[F]): scalaz.Functor[F] = | |
new scalaz.Functor[F] { | |
def map[A, B](fa: F[A])(f: A => B): F[B] = functor.map(f, fa) | |
} | |
def map[F[_], A, B](fa: F[A])(f: A => B)(implicit fun: Functor[F]): F[B] = | |
fun.map(fa)(f) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package obj.extend | |
import scalaz.std.function.fix | |
import obj.extend.typetags._ | |
import de.unimarburg.composition.reflection._ | |
// This encoding builds on references.scala - its aim is to remove the need | |
// for lenses by using mutable state directly. | |
// | |
// The core idea of the reference encoding is that Fix acts as box for the | |
// object to maintain reference equality in. | |
// | |
// Thus, val `x: Fix[F]; assert(x.extend(G) eq x)` but !(x.extend(G).out eq x.out) | |
// | |
// this should work, since the type parameter to Fix is erased anyway... | |
object newEncoding { | |
// We might need this later for references to super.. For self we can | |
// just use the boxed Obj[F] (Fix[F]) | |
class Ref[T](t: => T) { | |
private var value: T = null.asInstanceOf[T] | |
def dereference: T = { | |
if (value == null) { | |
value = t | |
} | |
value | |
} | |
def set(t: T): Unit = value = t | |
} | |
def Ref[T](t: => T): Ref[T] = new Ref[T](t) | |
implicit def autoDeref[T](ref: Ref[T]): T = ref.dereference | |
// This is now called Mixin, previously known as PreCoAlg | |
trait Mixin[-Self, +Prov, State] { | |
def apply: (Obj[X] forSome { type X <: Self }) => State => Prov | |
} | |
// Only classes can be instantiated. Previously called CompleteCoAlg | |
type Class[F, S] = Mixin[F, F, S] | |
// this is not a real fixed point any more, it's more of a reference container | |
// (this was previously called Fix, now Obj to match the name obj.extend) | |
// | |
// This might be related to the notion of Boxes in | |
// "Combining traits with boxes and ownership types in a Java-like setting" | |
// Lorenzo Bettini et al. | |
trait Obj[+F] { | |
def out: F | |
def extend[G, S2](other: Mixin[F with G, G, S2], state: S2)(implicit gT: TypeTag[G]): Obj[F with G] | |
// this is just a syntax helper | |
def extend[G: TypeTag](other: Mixin[F with G, G, Unit]): Obj[F with G] = this.extend(other, ()) | |
} | |
// wrap an arbitrary object to make it extensible | |
def Obj[F: TypeTag](f: F): Obj[F] = unfold[F, Unit](new Class[F, Unit] { | |
def apply = self => state => f | |
}, ()) | |
implicit def autoUnroll[F](f: Obj[F]): F = f.out | |
// this type casting is always safe at runtime since we only cast to type constructors | |
// and we maintain the invariant that { TypeTag[X]; _self: X } | |
def unfold[F, S1](co1: Class[F, S1], s1: S1)(implicit fT: TypeTag[F]): Obj[F] = new Obj[F] { self => | |
//type OpenSelf = (=> F) => F | |
// type: (F) => F -- we have to use Any here to allow later change | |
//private var openSelf: Any = (f: F) => co1.apply(self)(s1) //co1.apply(self.asInstanceOf[Ref[F]])(s1) | |
private var _self: F = co1.apply(self)(s1) | |
// TODO use for `super` | |
// type: Ref[F] | |
//private val self: Ref[_] = close | |
// type: TypeTag[F] | |
private var typetag: TypeTag[_] = fT | |
def out = _self | |
// XXX not used right now, left for `super` | |
// private def close: Ref[F] = fix[Ref[F]](f => Ref(openSelf.asInstanceOf[OpenSelf](f))) | |
def extend[G, S2](other: Mixin[F with G, G, S2], state: S2)(implicit gT: TypeTag[G]) = { | |
val fT = typetag.asInstanceOf[TypeTag[F]] | |
// here we cast to the already specialized type | |
val refinedSelf = self.asInstanceOf[Obj[F with G]] | |
// now actually refine `self` | |
// XXX is self in co1 bound to refinedSelf? I guess no | |
_self = mix(fT, gT)(_self, other.apply(refinedSelf)(state)) | |
typetag = intersectionTypeTag(fT, gT) | |
refinedSelf | |
} | |
} | |
} | |
object newEncodingTests { | |
import newEncoding._ | |
import de.unimarburg.composition.reflection._ | |
// Currently this.type is not supported | |
trait Counter { | |
def get: Int | |
def inc: Unit | |
} | |
trait SetCounter extends Counter { | |
def set(n: Int): Unit | |
} | |
trait ResetCounter { | |
def reset: Unit | |
} | |
object Counter extends Class[Counter, Int] { | |
def apply = self => n => new Counter { | |
private var count = n | |
def get: Int = count | |
def inc: Unit = count +=1 | |
} | |
} | |
object SetCounter extends Class[SetCounter, Int] { | |
def apply = self => n => new SetCounter { | |
private var count = n | |
def get: Int = count | |
def set(n: Int) = count = n | |
def inc = self.set(self.get + 1) | |
} | |
} | |
object ResetCounterRetroFit extends Mixin[ResetCounter with SetCounter, ResetCounter, Unit] { | |
def apply = self => _ => new ResetCounter { | |
def reset = self.set(0) | |
} | |
} | |
val c = unfold(Counter, 0) | |
println(c.get) | |
c.inc | |
c.inc | |
println(c.get) | |
assert(c.get == 2) | |
val sc = unfold(SetCounter, 0) | |
sc.inc | |
sc.inc | |
// SetCounter after two times incrementing | |
assert(sc.get == 2) | |
val sc2 = sc.extend(ResetCounterRetroFit, ()) | |
val scOut: ResetCounter with SetCounter = sc2.out | |
scOut.reset | |
// SetCounter after reset | |
assert(sc.get == 0) | |
// reference equality | |
assert(sc eq sc2) | |
def Mixin[Self, Prov, State](impl: (Obj[X] forSome { type X <: Self }) => State => Prov) = | |
new Mixin[Self, Prov, State] { | |
def apply = self => state => impl(self)(state) | |
} | |
val CoolSyntax = Mixin[ResetCounter with SetCounter, ResetCounter, Unit] { self => _ => | |
new ResetCounter { | |
def reset = self.set(0) | |
} | |
} | |
implicit class ClassOps[F: TypeTag, S](cls: Class[F, S]) { | |
def _new(implicit ev: Unit =:= S) = unfold[F, S](cls, ()) | |
def _new[A](a: A)(implicit ev: A =:= S) = unfold[F, S](cls, a) | |
def _new[A, B](a: A, b: B)(implicit ev: (A, B) =:= S) = unfold[F, S](cls, (a, b)) | |
def _new[A, B, C](a: A, b: B, c: C)(implicit ev: (A, B, C) =:= S) = unfold[F, S](cls, (a, b, c)) | |
def _new[A, B, C, D](a: A, b: B, c: C, d: D)(implicit ev: (A, B, C, D) =:= S) = unfold[F, S](cls, (a, b, c, d)) | |
} | |
import scala.language.reflectiveCalls | |
def implements[Prov] = new { | |
def apply(impl: (Obj[X] forSome { type X <: Prov }, Unit) => Prov) = | |
new Mixin[Prov, Prov, Unit] { | |
def apply = self => state => impl(self, state) | |
} | |
def requires[Req] = new { | |
def apply(impl: (Obj[X] forSome { type X <: Req with Prov }, Unit) => Prov) = | |
new Mixin[Req with Prov, Prov, Unit] { | |
def apply = self => state => impl(self, state) | |
} | |
} | |
def takes[State] = new { | |
def apply(impl: (Obj[X] forSome { type X <: Prov }, State) => Prov) = | |
new Mixin[Prov, Prov, State] { | |
def apply = self => state => impl(self, state) | |
} | |
} | |
} | |
val ResetCounter2 = implements[ResetCounter] requires[SetCounter] { case (self, _) => new ResetCounter { | |
def reset = self.set(0) | |
} | |
} | |
// In this setting it should be fairly easy to implement the type checking example | |
trait Exp | |
trait Lit extends Exp { def n: Int } | |
trait Add extends Exp { def lhs: Obj[Exp]; def rhs: Obj[Exp] } | |
val Lit = implements[Lit] takes[Int] { case (self, _n) => new Lit { | |
def n = _n | |
}} | |
val Add = implements[Add] takes[(Obj[Exp], Obj[Exp])] { case (self, (_lhs, _rhs)) => new Add { | |
def lhs = _lhs | |
def rhs = _rhs | |
}} | |
trait Type | |
case object NumT extends Type | |
trait Typed { | |
def tpe: Type | |
} | |
def addType(e: Obj[Exp], t: Type) = e extend (implements[Typed] { case (self, _) => new Typed { | |
def tpe = t | |
}}, ()) | |
def typecheck(e: Obj[Exp]): Obj[Exp with Typed] = e.out match { | |
case l: Lit => addType(e, NumT) | |
case a: Add => (typecheck(a.lhs).tpe, typecheck(a.rhs).tpe) match { | |
case (NumT, NumT) => addType(e, NumT) | |
case _ => sys error "Type Error" | |
} | |
} | |
def lit(n: Int) = unfold(Lit, n) | |
def add(l: Obj[Exp], r: Obj[Exp]) = unfold(Add, (l, r)) | |
val tree = add(add(add(lit(1), lit(2)), lit(3)), add(lit(4), lit(5))) | |
println(typecheck(tree).tpe) | |
// Pure awesomeness, we can lift any value to be extensible with this encoding? | |
// XXX To actually support case classes we need to improve the compose | |
// mechanism. Currently no params are passed to constructor calls | |
implicit def makeExtensible[F: TypeTag](f: F): Obj[F] = Obj[F](f) | |
locally { | |
case class Add(lhs: Obj[Exp], rhs: Obj[Exp]) extends Exp | |
case class Lit(n: Int) extends Exp | |
implicit def toFix(e: Exp): Obj[Exp] = Obj[Exp](e) | |
def typecheck(e: Obj[Exp]): Obj[Exp with Typed] = e.out match { | |
case l: Lit => addType(e, NumT) | |
case a: Add => (typecheck(a.lhs).tpe, typecheck(a.rhs).tpe) match { | |
case (NumT, NumT) => addType(e, NumT) | |
case _ => sys error "Type Error" | |
} | |
} | |
val tree: Obj[Exp] = Add(Add(Add(Lit(1), Lit(2)), Lit(3)), Add(Lit(4), Lit(5))) | |
println(typecheck(tree).tpe) | |
println(tree.asInstanceOf[Obj[Typed]].tpe) | |
} | |
trait OverrideMe { | |
def test: String | |
} | |
trait Printer { | |
def print: String | |
} | |
locally { | |
val BasePrinter = implements[Printer with OverrideMe] { case (self, _) => new Printer with OverrideMe { | |
def test = "Base" | |
def print = s"self: ${ test }, late: ${ self.test }" | |
}} | |
val OverridePrinter = implements[OverrideMe] requires[Printer] { case (self, _) => new OverrideMe { | |
def test = "Override1" | |
}} | |
val OverridePrinter2 = implements[OverrideMe] requires[Printer] { case (self, _) => new OverrideMe { | |
def test = "Override2" | |
}} | |
val base = BasePrinter._new | |
assert(base.print == "self: Base, late: Base") | |
base extend OverridePrinter | |
assert(base.print == "self: Base, late: Override1") | |
base extend OverridePrinter2 | |
assert(base.print == "self: Base, late: Override2") | |
// only takes 20ms, due to reduced reflection! | |
measure("Printing 10.000 times") { | |
for (i <- 1 to 10000) { | |
val x = base.print | |
} | |
} | |
def measure[T](text: String)(block: => T): T = { | |
val before = System.currentTimeMillis | |
val result = block | |
val after = System.currentTimeMillis | |
println(s"$text took " + (after - before) + "ms") | |
result | |
} | |
} | |
// Some rails like use cases | |
trait DomainItem { def name: String } | |
def DomainItem(n: String) = new DomainItem { def name = n } | |
trait Helpers { | |
def url: String | |
} | |
locally { | |
val Helpers = implements[Helpers] requires[DomainItem] { case (self, _) => new Helpers { | |
def url = s"http://www.myDomain.com/${ self.name }" | |
}} | |
def render(d: DomainItem): String = { | |
// look at this syntax: | |
val item = d extend Helpers | |
s"""<html><body><a href="${ item.url }">link</a></body></html>""" | |
} | |
println(render(DomainItem("FooBar"))) | |
} | |
// From Yehuda Katz blogpost: http://yehudakatz.com/2009/11/12/better-ruby-idioms/ | |
trait YaffleMethods { | |
def foo: Int | |
} | |
val YaffleMethods = implements[YaffleMethods] { case (self, _) => new YaffleMethods { | |
def foo = 42 | |
}} | |
trait Yaffle { | |
def acts_as_something: Obj[YaffleMethods] | |
} | |
val Yaffle = implements[Yaffle] { case (self, _) => new Yaffle { | |
def acts_as_something = self extend YaffleMethods | |
}} | |
// to be used as | |
trait ActiveRecord { def bar: Int} | |
val ActiveRecord = implements[ActiveRecord] requires[Yaffle] { case (self, _) => new ActiveRecord { | |
// this is only needed since we cannot tell Scala that self is now also of type Yaffle | |
// so we consciously shadow self | |
def bar = self.acts_as_something match { case self => | |
self.foo | |
} | |
}} | |
// TODO use <*> here. We also need facilities to immediately specify superclasses, like | |
// val ActiveRecord = implements[ActiveRecord] extend(Yaffle) | |
val ac = Yaffle._new extend ActiveRecord | |
println(ac.bar) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package obj | |
import scala.reflect.runtime.universe.{ TypeTag => RuTypeTag, WeakTypeTag => RuWeakTypeTag, _} | |
import scalaz.{ Lens, Functor } | |
import scalaz.LensFamily.{ lensId, firstLens, secondLens } | |
import scalaz.std.function.fix | |
import scalaz.syntax._ | |
import de.unimarburg.composition.WithF | |
import de.unimarburg.composition.reflection._ | |
import obj.extend.typetags._ | |
import obj.extend.functors._ | |
package object extend { | |
// enable implicit conversions and higher kinds for the whole project | |
private[extend] implicit val enableHigherKinds = scala.language.higherKinds | |
private[extend] implicit val enableImplicits = scala.language.implicitConversions | |
// alias to be imported automatically at user site | |
type TypeTag[T] = RuTypeTag[T] | |
type WeakTypeTag[T] = RuWeakTypeTag[T] | |
type CoAlg[+F[+_], S] = S => F[S] | |
// Following Oliveira's development on adding self references, but also adding | |
// open references to `super` | |
type Open[-Super, -Self <: Prov, +Prov] = (=> Super) => (=> Self) => Prov | |
// The general shape of a coalgebra (instead of distinguishing co and | |
// contravariant occurrences (as in pre algebras) we use an arbitrary | |
// functor taking the result type as argument | |
trait GenCoAlg[+F[_], R] { | |
def apply[S](l: Lens[S, R]): F[S] | |
} | |
/** | |
* A coalgebra polymorphic in the type of the state `S` | |
* In order to allow composition with other coalgebras the state is | |
* accessed via a lens. This way the state always is local to one | |
* coalgebra but can be passed as the whole frame. | |
*/ | |
// type OpenCoAlg[-Sup[+_], -Self[+X] <: Prov[X], +Prov[+_], R] = | |
// GenCoAlg[({ type λ[X] = Open[CoAlg[Sup, X], CoAlg[Self, X], CoAlg[Prov, X]] })#λ, R] | |
trait OpenCoAlg[-Base[+_], -Self[+X] <: Prov[X], +Prov[+_], S] { | |
def apply[T](l: Lens[T, S]): CoAlg[Base, T] => CoAlg[Self, T] => CoAlg[Prov, T] | |
} | |
/** | |
* A complete coalgebra provides what it requires and does not depend on | |
* a `super` coalgebra. | |
*/ | |
type CompleteCoAlg[F[+_], R] = OpenCoAlg[AnyF, F, F, R] | |
trait Fix[+F[+_]] { | |
def out: F[Fix[F]] | |
def extend[G[+_]: Functor, S2](co2: OpenCoAlg[F, (F WithF G)#Apply, G, S2], state2: S2)( | |
implicit gT: TypeTag[G[S2]]): Fix[(F WithF G)#Apply] | |
} | |
/** | |
* Mechanism to compose to OpenCoAlgs | |
* | |
* Required and provided interfaces are aggregated in intersection types. This | |
* and the later fixed point construction allow mutual recursive dependencies | |
* between OpenCoAlgebras. | |
*/ | |
def compose[ | |
Base1[+X], Self1[+X] <: Prov1[X], Prov1[+_], S1, | |
Self2[+X] <: Prov2[X], Prov2[+_], S2]( | |
co1: OpenCoAlg[Base1, Self1, Prov1, S1], | |
co2: OpenCoAlg[(Base1 WithF Prov1)#Apply, Self2, Prov2, S2])( | |
implicit | |
mix1: Base1 WithF Prov1, | |
mix2: Prov1 WithF Prov2) = | |
new OpenCoAlg[Base1, (Self1 WithF Self2)#Apply, (Prov1 WithF Prov2)#Apply, (S1, S2)] { | |
def apply[T](priv: Lens[T, (S1, S2)]) = base => self => state => { | |
// This one allows transitive access to all parents, as opposed to the | |
// simpler : | |
// val left = co1[T](priv andThen firstLens)(base)(self) | |
val left = (s: T) => mix1[T]( | |
base(s), | |
co1[T](priv andThen firstLens)(base)(self)(s) | |
) | |
val right = co2[T](priv andThen secondLens)(left)(self) | |
mix2[T](left(state), right(state)) | |
} | |
} | |
// fixed point of coalgebras (closing the self reference) | |
// The super reference is a dummy object | |
def close[F[+_], S](co: CompleteCoAlg[F, S]): CoAlg[F, S] = | |
fix[CoAlg[F, S]] { | |
self => state => co[S](lensId)(s => new {})(self)(state) | |
} | |
/** | |
* This function is at the heart of the thesis. At every layer of unfolding | |
* the extension point is inserted | |
* | |
* The implementation looks scary. This is only due to all the magic | |
* necessary to make type tags work. While reading simply skip everything | |
* that is related to `TypeTag`. | |
* | |
* Almost all `implicit val`s can be omitted by then `TypeTag[Nothing]` is | |
* inferred too often leading to the error: | |
* | |
* scala.tools.reflect.ToolBoxError: reflective toolbox failed due to | |
* unresolved free type variables | |
* | |
* So if you see this error, try adding some manually crafted type tags | |
* to the implicit resolution scope. | |
*/ | |
def unfold[F[+X] <: AnyF[X] : Functor, S1](co1: CompleteCoAlg[F, S1])( | |
// this way we get both type tags, F[_] and S | |
implicit fT: TypeTag[F[S1]]): S1 => Fix[F] = state1 => new Fix[F] { | |
// Here we only close the self reference, but this holds only for the known | |
// branch of the tree. The (?) branch still has a reference to the open coalgebra | |
lazy val out = map(close(co1)(state1))(unfold(co1)) | |
def extend[G[+_]: Functor, S2](co2: OpenCoAlg[F, (F WithF G)#Apply, G, S2], state2: S2)( | |
implicit gT: TypeTag[G[S2]]): Fix[(F WithF G)#Apply] = { | |
// All of this is hidden in the thesis | |
// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv | |
type BothS = (S1, S2) | |
type BothF[+X] = (F WithF G)#Apply[X] | |
// type tags needed for merging the functors and for materializing F WithF G | |
implicit val fWildcard: WeakTypeTag[F[_]] = wildcardTypeTag[F, S1] | |
implicit val gWildcard: WeakTypeTag[G[_]] = wildcardTypeTag[G, S2] | |
// Type tags necessary for recursive call | |
// -------------------------------------- | |
// extracted type tag for S | |
implicit val sT: TypeTag[S1] = typetags.unapplyTypeTag(fT) | |
implicit val s2T: TypeTag[S2] = typetags.unapplyTypeTag(gT) | |
// type tags for the composed interfaces and states | |
implicit val bothST = implicitly[TypeTag[(S1, S2)]] | |
implicit val fBothT: TypeTag[F[BothS]] = typetags.appliedTypeTag[F, S1, BothS] | |
implicit val gBothT: TypeTag[G[BothS]] = typetags.appliedTypeTag[G, S2, BothS] | |
implicit val bothFT = intersectionTypeTag[F[BothS], G[BothS]] | |
// instance of the composed functor | |
implicit val bothFunctors: Functor[(F WithF G)#Apply] = merge[F, G] | |
// this is Base1 in compose is AnyF, so we need mixF[AnyF, F] | |
val mix1 = WithF.anyL[F] | |
val mix2 = implicitly[F WithF G] | |
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
unfold[(F WithF G)#Apply, (S1, S2)]( | |
compose[AnyF, F, F, S1, (F WithF G)#Apply, G, S2](co1, co2)(mix1, mix2) | |
).apply((state1, state2)) | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package obj.extend | |
import scalaz.{ Lens, Functor, State } | |
import de.unimarburg.composition.WithF | |
import obj.extend.functors._ | |
package object syntax { | |
trait Mixin[Sup[+_], Sel[+X] <: Pro[X], Pro[+_], R] extends OpenCoAlg[Sup, Sel, Pro, R] { | |
type State = R | |
type Super[+X] = Sup[X] | |
type Self[+X] = Sel[X] | |
type Prov[+X] = Pro[X] | |
def apply[S](l: Lens[S, R]) = sup => self => impl(l, sup, self) | |
def impl[S](implicit l: Lens[S, R], sup: CoAlg[Sup, S], self: CoAlg[Self, S]): CoAlg[Prov, S] | |
// trying to improve the interface on lenses | |
implicit class LensOps[S, R](lens: Lens[S, R])(implicit state: S) { | |
def ? = lens.get(state) | |
def :=(newR: R): S = lens.set(state, newR) | |
} | |
implicit def applyState[S, R](st: scalaz.State[S, R])(implicit s: S): S = st(s)._1 | |
implicit def applyFun[S](f: S => S)(implicit s: S): S = f(s) | |
implicit def liftIntoLens[S](f: R => R)(implicit l: Lens[S, R], s: S) = l.mod(f, s) | |
implicit def autoState[S, F[+_]](coalg: CoAlg[F, S])(implicit s: S): F[S] = coalg(s) | |
} | |
// Syntax for defining pre co algs without having to give explicit types | |
trait Mixin2[Sup[+_], Sel[+X] <: Pro[X], Pro[+_], R] extends OpenCoAlg[Sup, Sel, Pro, R] { | |
def impl[S]: Lens[S, R] => CoAlg[Sup, S] => CoAlg[Sel, S] => CoAlg[Pro, S] | |
def apply[S](l: Lens[S, R]) = sup => self => impl(l)(sup)(self) | |
} | |
trait CompleteMixin2[Pro[+X] <: AnyF[X], R] extends Mixin2[AnyF, Pro, Pro, R] { | |
def _new(s: R)(implicit f: Functor[Pro], ft: TypeTag[Pro[R]]) = | |
unfold[Pro, R](this).apply(s) | |
} | |
implicit class CoAlgOps[F[+X] <: AnyF[X], S](co: CompleteCoAlg[F, S]) { | |
def _new(s: S)(implicit f: Functor[F], ft: TypeTag[F[S]]) = unfold[F, S](co).apply(s) | |
} | |
implicit class OpenCoAlgOps[Base1[+X], Self1[+X] <: Prov1[X], Prov1[+_], S1](self: OpenCoAlg[Base1, Self1, Prov1, S1]) { | |
def <+>[Self2[+X] <: Prov2[X], Prov2[+_], S2](other: OpenCoAlg[(Base1 WithF Prov1)#Apply, Self2, Prov2, S2])( | |
implicit mix1: Base1 WithF Prov1, mix2: Prov1 WithF Prov2) = compose[Base1, Self1, Prov1, S1, Self2, Prov2, S2](self, other) | |
} | |
// should probably be: implicit def unroll[F[+X] <: G[X], G[+_]](f: Fix[F]): G[Fix[F]] = f.out | |
// but this wont work | |
implicit def unroll[F[+X]](f: Fix[F]) = f.out | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment