Skip to content

Commit

Permalink
WIP -- bring context to all monads.
Browse files Browse the repository at this point in the history
  • Loading branch information
rssh committed Nov 2, 2021
1 parent afa62ed commit dd3ddf9
Show file tree
Hide file tree
Showing 12 changed files with 133 additions and 30 deletions.
32 changes: 17 additions & 15 deletions js/src/main/scala/cps/monads/jsfuture/JSFuture.scala
Original file line number Diff line number Diff line change
Expand Up @@ -116,35 +116,37 @@ class JSFuture[T](

}

object JSFuture:

given JSFutureCpsMonad: CpsTryMonad[JSFuture] with CpsMonadInstanceContext[JSFuture] with

given JSFutureCpsMonad: CpsTryMonad[JSFuture] with


def pure[A](a:A): JSFuture[A] = new JSFuture[A](js.undefined, Future successful a)
def pure[A](a:A): JSFuture[A] = new JSFuture[A](js.undefined, Future successful a)

def map[A,B](fa: JSFuture[A])(f: A=>B): JSFuture[B] =
def map[A,B](fa: JSFuture[A])(f: A=>B): JSFuture[B] =
fa.map(f)

def flatMap[A,B](fa: JSFuture[A])(f: A=> JSFuture[B]): JSFuture[B] =
def flatMap[A,B](fa: JSFuture[A])(f: A=> JSFuture[B]): JSFuture[B] =
fa.flatMap(f)

def error[A](ex:Throwable): JSFuture[A] = new JSFuture[A](js.undefined, Future failed ex)
def error[A](ex:Throwable): JSFuture[A] = new JSFuture[A](js.undefined, Future failed ex)

override def mapTry[A,B](fa: JSFuture[A])(f: Try[A]=>B): JSFuture[B] =
override def mapTry[A,B](fa: JSFuture[A])(f: Try[A]=>B): JSFuture[B] =
fa.mapTry(f)

def flatMapTry[A,B](fa: JSFuture[A])(f: Try[A]=> JSFuture[B]): JSFuture[B] =
def flatMapTry[A,B](fa: JSFuture[A])(f: Try[A]=> JSFuture[B]): JSFuture[B] =
fa.flatMapTry(f)

end JSFutureCpsMonad


given CpsMonadConversion[Future,JSFuture] with
def apply[T](ft:Future[T]): JSFuture[T] =
given CpsMonadConversion[Future,JSFuture] with
def apply[T](ft:Future[T]): JSFuture[T] =
new JSFuture(js.undefined, ft)

given CpsMonadConversion[JSFuture, Future] with
def apply[T](ft:JSFuture[T]): Future[T] = ft.future
given CpsMonadConversion[JSFuture, Future] with
def apply[T](ft:JSFuture[T]): Future[T] = ft.future

given CpsMonadConversion[js.Promise, JSFuture] with
def apply[T](ft:js.Promise[T]): JSFuture[T] =
given CpsMonadConversion[js.Promise, JSFuture] with
def apply[T](ft:js.Promise[T]): JSFuture[T] =
new JSFuture(js.undefined, ft.toFuture)

10 changes: 9 additions & 1 deletion js/src/test/scala/cps/ComputationBound.scala
Original file line number Diff line number Diff line change
Expand Up @@ -155,10 +155,12 @@ object ComputationBound {

}

implicit object ComputationBoundAsyncMonad extends CpsAsyncMonad[ComputationBound] {
implicit object ComputationBoundAsyncMonad extends CpsAsyncMonad[ComputationBound] {

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 @@ -198,6 +200,12 @@ implicit object ComputationBoundAsyncMonad extends CpsAsyncMonad[ComputationBoun
def spawn[A](op: => ComputationBound[A]): ComputationBound[A] =
ComputationBound.spawn(op)

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

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

}

case class Thunk[T](thunk: ()=>ComputationBound[T]) extends ComputationBound[T] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ given CompletableFutureCpsMonad: CpsSchedulingMonad[CompletableFuture] with CpsM
}
retval

def spawn[A](op: Context ?=> CompletableFuture[A]): CompletableFuture[A] =
def spawn[A](op: => CompletableFuture[A]): CompletableFuture[A] =
val r = new CompletableFuture[A]()
CompletableFuture.runAsync{()=>
try
Expand Down
6 changes: 3 additions & 3 deletions shared/src/main/scala/cps/CpsMonad.scala
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ trait CpsConcurrentMonad[F[_]] extends CpsAsyncMonad[F] {
/**
* spawn execution of operation in own execution flow.
**/
def spawnEffect[A](op: Context ?=>F[A]): F[Spawned[A]]
def spawnEffect[A](op: =>F[A]): F[Spawned[A]]

/**
* join the `op` computation: i.e. result is `op` which will become available
Expand Down Expand Up @@ -345,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: Context ?=> F[A]): F[A]
def spawn[A](op: => F[A]): F[A]

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

/**
Expand Down
43 changes: 40 additions & 3 deletions shared/src/main/scala/cps/macros/Async.scala
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ object Async {
def transformMonad[F[_]:Type,T:Type](f: Expr[T], dm: Expr[CpsMonad[F]])(using Quotes): Expr[F[T]] =
import quotes.reflect._
val flags = adoptFlags(f, dm)
val DEBUG = true
try
if flags.printCode then
try
Expand All @@ -88,14 +89,30 @@ object Async {
val r = WithOptExprProxy("cpsMonad", dm){
dm =>
val cpsExpr = rootTransform[F,T](f,dm,memoization,flags,observatory,0, None)
if (dm.asTerm.tpe <:< TypeRepr.of[CpsEffectMonad[F]]) then
if (DEBUG) {
TransformUtil.dummyMapper(cpsExpr.transformed.asTerm, Symbol.spliceOwner)
}
val retval = if (dm.asTerm.tpe <:< TypeRepr.of[CpsEffectMonad[F]]) then
'{ ${dm.asExprOf[CpsEffectMonad[F]]}.flatDelay(${cpsExpr.transformed}) }
else if (dm.asTerm.tpe <:< TypeRepr.of[CpsSchedulingMonad[F]]) then
'{ ${dm.asExprOf[CpsSchedulingMonad[F]]}.spawn(${cpsExpr.transformed}) }
else
if (flags.debugLevel > 10) then
println(s"dm.asTerm.tpe = ${dm.asTerm.tpe.show} not implements CpsEffectMonad[F], f=$Type[F].show")
cpsExpr.transformed
if (DEBUG) {
try {
TransformUtil.dummyMapper(retval.asTerm, Symbol.spliceOwner)
}catch{
case ex: Throwable =>
println(s"failed term: ${retval.show}")
throw ex
}
}
retval
}
if (DEBUG) {
TransformUtil.dummyMapper(r.asTerm, Symbol.spliceOwner)
}
if (flags.printCode)
try
Expand Down Expand Up @@ -178,9 +195,9 @@ object Async {
parent: Option[TransformationContext[_,_]])(
using Quotes): CpsExpr[F,T] =
val tType = summon[Type[T]]
import quotes.reflect._
import quotes.reflect._
val cpsCtx = TransformationContext[F,T](f,tType,dm,optMemoization,flags,observatory,nesting,parent)
f match
val retval = f match
case '{ if ($cond) $ifTrue else $ifFalse } =>
IfTransform.run(cpsCtx, cond, ifTrue, ifFalse)
case '{ while ($cond) { $repeat } } =>
Expand Down Expand Up @@ -234,6 +251,16 @@ object Async {
println("fTree:"+fTree)
throw MacroError(s"language construction is not supported: ${fTree}", f)
}
if (true) then // check
val dummyMap = new TreeMap() {}
try {
val x = dummyMap.transformTerm(retval.transformed.asTerm)(Symbol.spliceOwner)
}catch{
case ex: Throwable =>
println(s"Here, input term is ${f.show}, tree:${f.asTerm}")
throw ex;
}
retval


def nestTransform[F[_]:Type,T:Type,S:Type](f:Expr[S],
Expand Down Expand Up @@ -265,8 +292,18 @@ object Async {
report.throwError(s"lambda expected, have: ${f}", cexpr)

def transformNotInlined(t: Term): Term =
val DEBUG = true
val (oldParams, body, nestFun) = extractLambda(t)
val transformed = transformImpl[F,T](body.asExprOf[T])
if (DEBUG) {
try {
TransformUtil.dummyMapper(transformed.asTerm,Symbol.spliceOwner)
} catch {
case ex: Throwable =>
println(s"contextLambda, body=${body}")
throw ex;
}
}
val oldValDef = oldParams.head
val mt = MethodType(List(oldValDef.name))( _ => List(oldValDef.tpt.tpe), _ => TypeRepr.of[F[T]])
val nLambda = Lambda(Symbol.spliceOwner, mt, (owner, params) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,6 @@ object TransformUtil:
var wasError = false
val checker = new TreeMap() {


override def transformTerm(tree: Term)(owner: Symbol): Term =
try {
super.transformTerm(tree)(owner)
Expand Down
4 changes: 2 additions & 2 deletions shared/src/main/scala/cps/macros/forest/TryTransform.scala
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class TryTransform[F[_]:Type,T:Type](cpsCtx: TransformationContext[F,T]):

def makeRestoreExpr(): Expr[Throwable => F[T]] =
val nCaseDefs = makeAsyncCaseDefs()
val restoreExpr = '{ (ex: Throwable) => ${Match('ex.asTerm, nCaseDefs).asExprOf[F[T]]} }
val restoreExpr = '{ (ex: Throwable) => ${Match('ex.asTerm, nCaseDefs).changeOwner(Symbol.spliceOwner).asExprOf[F[T]]} }
restoreExpr.asExprOf[Throwable => F[T]]


Expand Down Expand Up @@ -83,7 +83,7 @@ class TryTransform[F[_]:Type,T:Type](cpsCtx: TransformationContext[F,T]):
val nBody = '{ ${monad}.pure($syncBody) }.asTerm
CpsExpr.async[F,T](cpsCtx.monad,
Try(nBody, makeAsyncCaseDefs(), None).asExprOf[F[T]]
)
)
case Some(cpsFinalizer) =>
if (cpsCaseDefs.isEmpty)
cpsBody.syncOrigin match
Expand Down
21 changes: 20 additions & 1 deletion shared/src/main/scala/cps/macros/forest/WhileTransform.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ object WhileTransform:
val cpsRepeat = Async.nestTransform(repeat, cpsCtx)
val isAsync = cpsCond.isAsync || cpsRepeat.isAsync

val DEBUG = true

val unitBuilder = {
if (!cpsCond.isAsync)
if (!cpsRepeat.isAsync)
Expand All @@ -31,6 +33,7 @@ object WhileTransform:
val term = While(cpsCond.syncOrigin.get.asTerm, cpsRepeat.syncOrigin.get.asTerm)
CpsExpr.sync(monad, term.asExprOf[T], true)
else
/*
CpsExpr.async[F,Unit](monad,
// TODO: add name to whileFun ?
'{
Expand All @@ -44,8 +47,13 @@ object WhileTransform:
}
_whilefun()
})
*/
CpsExpr.async[F,Unit](monad, '{
cps.runtime.WhileHelper.w01(${monad},${cond},${cpsRepeat.transformed})
})
else // (cpsCond.isAsync)
if (!cpsRepeat.isAsync) {
/*
CpsExpr.async[F,Unit](monad,
'{
def _whilefun(): F[Unit] = {
Expand All @@ -61,8 +69,14 @@ object WhileTransform:
}
_whilefun()
})
*/
CpsExpr.async[F,Unit](monad, '{
cps.runtime.WhileHelper.w10(${monad},${cpsCond.transformed},${repeat})
})
} else {
CpsExpr.async[F,Unit](monad,
/*
* TODO: submit big to doffy
val retval = CpsExpr.async[F,Unit](monad,
'{
def _whilefun(): F[Unit] = {
${cpsCond.flatMap[Unit]('{ (c: Boolean) =>
Expand All @@ -78,6 +92,11 @@ object WhileTransform:
}
_whilefun()
})
*/
CpsExpr.async[F,Unit](monad,
'{
cps.runtime.WhileHelper.w11(${monad},${cpsCond.transformed},${cpsRepeat.transformed})
})
}
}
unitBuilder.asInstanceOf[CpsExpr[F,T]]
Expand Down
5 changes: 5 additions & 0 deletions shared/src/main/scala/cps/macros/misc/WithOptExprProxy.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,16 @@
package cps.macros.misc

import scala.quoted._
import cps.macros.forest.*

object WithOptExprProxy:

def apply[T:Type,S:Type](name: String, originExpr: Expr[T])(buildExpr: Expr[T] => Expr[S])(using Quotes): Expr[S] =
import quotes.reflect.*
val DEBUG = true
if (DEBUG) {
TransformUtil.dummyMapper(originExpr.asTerm, Symbol.spliceOwner)
}
originExpr.asTerm match
case Ident(_) => buildExpr(originExpr)
case originTerm =>
Expand Down
4 changes: 2 additions & 2 deletions shared/src/main/scala/cps/monads/FutureAsyncMonad.scala
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,12 @@ given FutureAsyncMonad(using ExecutionContext): CpsSchedulingMonad[Future] with
source(p.complete(_))
p.future

def spawn[A](op: Context ?=> F[A]): F[A] =
def spawn[A](op: => F[A]): F[A] =
val p = Promise[A]
summon[ExecutionContext].execute{
() =>
try
p.completeWith(op(using this))
p.completeWith(op)
catch
case NonFatal(ex) =>
p.complete(Failure(ex))
Expand Down
33 changes: 33 additions & 0 deletions shared/src/main/scala/cps/runtime/WhileHelper.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package cps.runtime

import cps.*

object WhileHelper {

def w11[F[_]](m:CpsMonad[F], cond: =>F[Boolean], repeat: =>F[Unit]):F[Unit] =
m.flatMap(cond){ c =>
if (c) then
m.flatMap(repeat)( u => w11(m, cond,repeat) )
else
m.pure(())
}


def w10[F[_]](m:CpsMonad[F], cond: =>F[Boolean], repeat: => Unit):F[Unit] =
m.flatMap(cond){ c =>
if (c) {
repeat
w10(m, cond,repeat)
} else {
m.pure(())
}
}

def w01[F[_]](m:CpsMonad[F], cond: =>Boolean, repeat: => F[Unit]):F[Unit] =
if (cond) {
m.flatMap(repeat)( u => w01(m,cond,repeat))
} else {
m.pure(())
}

}
2 changes: 1 addition & 1 deletion shared/src/test/scala/cps/pe/PureEffect.scala
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ given PureEffectCpsMonad: CpsConcurrentEffectMonad[PureEffect] with CpsMonadInst
}
FutureThunk(()=>p.future)

def spawnEffect[A](op: Context ?=> PureEffect[A]): PureEffect[Spawned[A]] =
def spawnEffect[A](op: => PureEffect[A]): PureEffect[Spawned[A]] =
FutureThunk{() =>
given ExecutionContext = ExecutionContext.global
Future(
Expand Down

0 comments on commit dd3ddf9

Please sign in to comment.