Skip to content

Commit

Permalink
monad-instance context
Browse files Browse the repository at this point in the history
  • Loading branch information
rssh committed Nov 2, 2021
1 parent a74c5d9 commit afa62ed
Show file tree
Hide file tree
Showing 15 changed files with 85 additions and 56 deletions.
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ lazy val cps = crossProject(JSPlatform, JVMPlatform)
.disablePlugins(SitePreviewPlugin)
.jvmSettings(
scalacOptions ++= Seq( "-unchecked", "-Ydebug-trace", "-Ydebug-names", "-Xprint-types",
"-Ydebug", "-uniqid", "-Ycheck:macros", "-Yprint-syms" ),
"-Ydebug", "-uniqid", "-Xcheck-macros", "-Ycheck:macro", "-Yprint-syms" ),
// -explain
// -Ydebug-error
// -Ydebug-tree-with-id -1
Expand Down
4 changes: 2 additions & 2 deletions jvm/src/main/scala/cps/monads/CompletableFutureCpsMonad.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import scala.util.Success
import scala.util.control.NonFatal


given CompletableFutureCpsMonad: CpsSchedulingMonad[CompletableFuture] with {
given CompletableFutureCpsMonad: CpsSchedulingMonad[CompletableFuture] with CpsMonadInstanceContext[CompletableFuture] with {

def pure[T](t:T):CompletableFuture[T] =
CompletableFuture.completedFuture(t)
Expand Down Expand Up @@ -90,7 +90,7 @@ given CompletableFutureCpsMonad: CpsSchedulingMonad[CompletableFuture] with {
}
retval

def spawn[A](op: => CompletableFuture[A]): CompletableFuture[A] =
def spawn[A](op: Context ?=> CompletableFuture[A]): CompletableFuture[A] =
val r = new CompletableFuture[A]()
CompletableFuture.runAsync{()=>
try
Expand Down
14 changes: 10 additions & 4 deletions jvm/src/test/scala/cps/ComputationBound.scala
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,7 @@ object ComputationBound {
spawn(f)

def lazyMemoize[T](f: ComputationBound[T]): ComputationBound[T] =
Thunk(() => spawn(f))

Thunk(() => spawn(f))

}

Expand All @@ -123,6 +122,8 @@ implicit object ComputationBoundAsyncMonad extends CpsAsyncMonad[ComputationBoun

type WF[T] = ComputationBound[T]

override type Context = ComputationBound.type

def pure[T](value:T): ComputationBound[T] = ComputationBound.pure(value)

def finalAwait[T](t:ComputationBound[T]):Try[T] = t.run()
Expand Down Expand Up @@ -158,12 +159,17 @@ implicit object ComputationBoundAsyncMonad extends CpsAsyncMonad[ComputationBoun
def adoptCallbackStyle[A](source: (Try[A]=>Unit) => Unit):ComputationBound[A] =
ComputationBound.asyncCallback(source)

def spawn[A](op: => ComputationBound[A]): ComputationBound[A] =
ComputationBound.spawn(op)
def spawn[A](op: Context ?=> ComputationBound[A]): ComputationBound[A] =
ComputationBound.spawn(op(using ComputationBound))

def fulfill[T](t: ComputationBound[T], timeout: Duration): Option[Try[T]] =
t.fulfill(timeout)

def apply[T](f: Context => ComputationBound[T]): ComputationBound[T] =
f(ComputationBound)

def adoptAwait[A](c: Context, v:ComputationBound[A]):ComputationBound[A] =
v

}

Expand Down
2 changes: 1 addition & 1 deletion jvm/src/test/scala/cps/chessboard/knightour.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import cps._

//TODO: move to cps.runtime

given CpsMonad[List] with
given CpsMonad[List] with CpsMonadInstanceContext[List] with

def pure[T](t:T):List[T] =
List(t)
Expand Down
33 changes: 21 additions & 12 deletions shared/src/main/scala/cps/CpsMonad.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ trait CpsMonad[F[_]] extends CpsAwaitable[F] {

type WF[X] = F[X]

/**
* ''
**/
type Context

/**
* Pure - wrap value `t` inside monad.
*
Expand All @@ -38,9 +43,21 @@ trait CpsMonad[F[_]] extends CpsAwaitable[F] {
**/
def flatMap[A,B](fa:F[A])(f: A=>F[B]):F[B]

/**
* run op in the context environment.
**/
def apply[T](op: Context => F[T]): F[T]

/**
* adopt monadic value in await to current context.
*
**/
def adoptAwait[A](c:Context, fa:F[A]):F[A]

}



/**
* Marker typeclass for wrappers, which we can await.
* Such traits can be not monads itself (for example, its impossible to set monad structure over js.Promise)
Expand Down Expand Up @@ -231,7 +248,7 @@ trait CpsEffectMonad[F[_]] extends CpsMonad[F] {
* representing pure as eager function is a simplification of semantics,
* real binding to monads in math sence, should be with `delay` instead `pure`
**/
def delay[T](x: =>T):F[T] = map(delayedUnit)(_ => x)
def delay[T](x: => T):F[T] = map(delayedUnit)(_ => x)

/**
* shortcat for delayed evaluation of effect.
Expand Down Expand Up @@ -263,7 +280,7 @@ trait CpsConcurrentMonad[F[_]] extends CpsAsyncMonad[F] {
/**
* spawn execution of operation in own execution flow.
**/
def spawnEffect[A](op: =>F[A]): F[Spawned[A]]
def spawnEffect[A](op: Context ?=>F[A]): F[Spawned[A]]

/**
* join the `op` computation: i.e. result is `op` which will become available
Expand Down Expand Up @@ -328,7 +345,7 @@ trait CpsSchedulingMonad[F[_]] extends CpsConcurrentMonad[F] {
* schedule execution of op somewhere, immediatly.
* Note, that characteristics of scheduler can vary.
**/
def spawn[A](op: =>F[A]): F[A]
def spawn[A](op: Context ?=> F[A]): F[A]

/***
* In eager monad, spawned process can be represented by F[_]
Expand All @@ -338,7 +355,7 @@ trait CpsSchedulingMonad[F[_]] extends CpsConcurrentMonad[F] {
/**
* representation of spawnEffect as immediate operation.
**/
def spawnEffect[A](op: =>F[A]): F[F[A]] =
def spawnEffect[A](op: Context ?=> F[A]): F[F[A]] =
pure(spawn(op))

/**
Expand All @@ -347,14 +364,6 @@ trait CpsSchedulingMonad[F[_]] extends CpsConcurrentMonad[F] {
def join[A](op: Spawned[A]): F[A] = op



}

trait CpsContextMonad[F[_]] extends CpsMonad[F] {

type Context

def in[T]( f: Context => F[T] ): F[T]


}
1 change: 0 additions & 1 deletion shared/src/main/scala/cps/CpsMonadContextProvider.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,3 @@ trait CpsMonadContextProvider[F[_]]:

def contextualize[A](fa: Context => F[A]): F[A]


20 changes: 20 additions & 0 deletions shared/src/main/scala/cps/CpsMonadInstanceContext.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package cps


/**
* trait, which should be mixed in CpsMoand
* to provide instance of CpsMonad as context.
**/
trait CpsMonadInstanceContext[F[_]] {

this: CpsMonad[F] =>

override type Context = this.type

def apply[T](f: Context => F[T]): F[T] =
f(this)

def adoptAwait[A](c:Context, fa:F[A]):F[A] =
fa
}

22 changes: 8 additions & 14 deletions shared/src/main/scala/cps/macros/Async.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,34 +19,28 @@ import cps.macros.observatory.*

object Async {

class InferAsyncArg[F[_]](using am:CpsMonad[F]) {
class InferAsyncArg[F[_]](using val am:CpsMonad[F]) {

transparent inline def apply[T](inline expr: T) =
transform[F,T](using am)(expr)
transparent inline def apply[T](inline expr: am.Context ?=> T) =
//transform[F,T](using am)(expr)
am.apply(transformContextLambda(expr))

// ?=> cause compilre crash.
transparent inline def in[T](using mc: CpsMonadContextProvider[F] )(inline expr: mc.Context => T ): F[T] =

transparent inline def in[T](using mc: CpsMonadContextProvider[F] )(inline expr: mc.Context ?=> T ): F[T] =
mc.contextualize(transformContextLambda(expr))

}

inline def async[F[_]](using am:CpsMonad[F]): InferAsyncArg[F] =
new InferAsyncArg[F]

class InferAsyncContextArg[F[_],C](using am:CpsContextMonad[F] { type Context = C }) {

transparent inline def apply[T](inline expr: C => T) =
am.in(transformContextLambda(expr))

}


transparent inline def transform[F[_], T](using m: CpsMonad[F])(inline expr: T) =
${
Async.transformImpl[F,T]('expr)
}

transparent inline def transformContextLambda[F[_],T,C](inline expr: C => T)(using m: CpsMonad[F]): C => F[T] =
transparent inline def transformContextLambda[F[_],T,C](inline expr: C ?=> T)(using m: CpsMonad[F]): C => F[T] =
${
Async.transformContextLambdaImpl[F,T,C]('expr)
}
Expand Down Expand Up @@ -249,7 +243,7 @@ object Async {
cpsCtx.flags,cpsCtx.observatory,cpsCtx.nesting+1, Some(cpsCtx))


def transformContextLambdaImpl[F[_]:Type, T:Type, C:Type](cexpr: Expr[C => T])(using Quotes): Expr[C => F[T]] =
def transformContextLambdaImpl[F[_]:Type, T:Type, C:Type](cexpr: Expr[C ?=> T])(using Quotes): Expr[C => F[T]] =
import quotes.reflect._

def inInlined(t: Term, f: Term => Term): Term =
Expand Down
20 changes: 10 additions & 10 deletions shared/src/main/scala/cps/macros/forest/CpsTree.scala
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ trait CpsTreeScope[F[_], CT] {
val wPrevOtpe = TransformUtil.veryWiden(prev.otpe.widen)
val tmapTerm = untmapTerm.appliedToTypes(List(wPrevOtpe,otpe))
val r = tmapTerm.appliedToArgss(
List(List(prev.transformed),
List(List(prev.transformed.changeOwner(Symbol.spliceOwner)),
List(
Lambda(
Symbol.spliceOwner,
Expand Down Expand Up @@ -340,7 +340,7 @@ trait CpsTreeScope[F[_], CT] {
val tpFlatMapTerm = untpFlatMapTerm.appliedToTypes(List(wPrevOtpe,wOtpe))
val r = tpFlatMapTerm.appliedToArgss(
List(
List(prev.castOtpe(wPrevOtpe).transformed),
List(prev.castOtpe(wPrevOtpe).transformed.changeOwner(Symbol.spliceOwner)),
List(
Lambda(
Symbol.spliceOwner,
Expand Down Expand Up @@ -382,13 +382,13 @@ trait CpsTreeScope[F[_], CT] {
if (prevs.isEmpty)
last.transformed
else
Block(prevs.toList, last.transformed)
Block(prevs.toList, last.transformed).changeOwner(Symbol.spliceOwner)

override def syncOrigin: Option[Term] =
if prevs.isEmpty then
last.syncOrigin
else
last.syncOrigin map (l => Block(prevs.toList,l))
last.syncOrigin map (l => Block(prevs.toList,l).changeOwner(Symbol.spliceOwner))


// TODO: pass other cake ?
Expand Down Expand Up @@ -544,14 +544,14 @@ trait CpsTreeScope[F[_], CT] {
val result = nested match
case BlockCpsTree.Matcher(prevs,last) =>
val lastTerm = last.syncOrigin.getOrElse(last.transformed)
Block(nValDef +: prevs.toList, lastTerm.asInstanceOf[Term])
Block(nValDef +: prevs.toList, lastTerm.asInstanceOf[Term]).changeOwner(Symbol.spliceOwner)
case _ =>
val next = nested.syncOrigin.getOrElse(nested.transformed)
appendValDefToNextTerm(nValDef, next.asInstanceOf[Term])
result

def appendValDefToNextTerm(valDef: ValDef, next:Term): Term =
next match
next.changeOwner(valDef.symbol.owner) match
case x@Lambda(params,term) => Block(List(valDef), x)
case Block(stats, last) => Block(valDef::stats, last)
case other => Block(List(valDef), other)
Expand Down Expand Up @@ -592,15 +592,15 @@ trait CpsTreeScope[F[_], CT] {
case Block(xStats, xLast) =>
y match
case Block(yStats, yLast) =>
Block((xStats :+ xLast) ++ yStats, yLast)
Block((xStats :+ xLast) ++ yStats, yLast).changeOwner(Symbol.spliceOwner)
case yOther =>
Block(xStats :+ xLast, yOther)
Block(xStats :+ xLast, yOther).changeOwner(Symbol.spliceOwner)
case xOther =>
y match
case Block(yStats, yLast) =>
Block(xOther::yStats, yLast)
Block(xOther::yStats, yLast).changeOwner(Symbol.spliceOwner)
case yOther =>
Block(xOther::Nil, yOther)
Block(xOther::Nil, yOther).changeOwner(Symbol.spliceOwner)
}
}

Expand Down
7 changes: 4 additions & 3 deletions shared/src/main/scala/cps/macros/forest/ValDefTransform.scala
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ object ValDefTransform:
Block( prevStats ++: (valDef +: statements), last)
case other =>
Block( prevStats ++: List(valDef), other)
outputTerm.asExprOf[T]
outputTerm.changeOwner(Symbol.spliceOwner).asExprOf[T]
}


Expand All @@ -192,7 +192,7 @@ object ValDefTransform:
Block( prev.map(_.extract) ++: valDef +: stats, e)
case other =>
Block( prev.map(_.extract) ++: List(valDef) , other)
block.asExprOf[F[T]]
block.changeOwner(Symbol.spliceOwner).asExprOf[F[T]]

}

Expand All @@ -213,11 +213,12 @@ object ValDefTransform:
if (prev.isEmpty) {
term
} else {
term match
val retval = term match
case Block(stats, expr) =>
Block(prev.map(_.extract) ++: stats, expr)
case other =>
Block(prev.toList.map(_.extract) , other)
retval.changeOwner(Symbol.spliceOwner)
}


6 changes: 3 additions & 3 deletions shared/src/main/scala/cps/monads/FutureAsyncMonad.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import scala.util.control._
/**
* Default CpsMonad implementation for `Future`
**/
given FutureAsyncMonad(using ExecutionContext): CpsSchedulingMonad[Future] with
given FutureAsyncMonad(using ExecutionContext): CpsSchedulingMonad[Future] with CpsMonadInstanceContext[Future] with

type F[+T] = Future[T]

Expand Down Expand Up @@ -43,12 +43,12 @@ given FutureAsyncMonad(using ExecutionContext): CpsSchedulingMonad[Future] with
source(p.complete(_))
p.future

def spawn[A](op: => F[A]): F[A] =
def spawn[A](op: Context ?=> F[A]): F[A] =
val p = Promise[A]
summon[ExecutionContext].execute{
() =>
try
p.completeWith(op)
p.completeWith(op(using this))
catch
case NonFatal(ex) =>
p.complete(Failure(ex))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ class TestDeferredInContextMonad:
//Compiler crash in dotty-3.1.0-RC1
@Test def testSimpleContext(): Unit =
var x = 0
val c = async.in{ scope =>
val c = async.in{ scope ?=>
scope.deferr{ x = 1 }
1
}
Expand Down
2 changes: 1 addition & 1 deletion shared/src/test/scala/cps/gopherlike/TestSL.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class TestSL:
// should not be covariant
class MyF[T]

given CpsMonad[MyF] with
given CpsMonad[MyF] with CpsMonadInstanceContext[MyF] with
def pure[A](a:A): MyF[A] = ???
def map[A,B](fa:MyF[A])(f:A=>B):MyF[B] = ???
def flatMap[A,B](fa:MyF[A])(f:A=>MyF[B]):MyF[B] = ???
Expand Down
Loading

0 comments on commit afa62ed

Please sign in to comment.