Skip to content

Commit

Permalink
added contrct provider
Browse files Browse the repository at this point in the history
  • Loading branch information
rssh committed Sep 18, 2021
1 parent e02f511 commit 2f744f2
Show file tree
Hide file tree
Showing 7 changed files with 169 additions and 12 deletions.
6 changes: 4 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//val dottyVersion = "3.0.2-RC1-bin-SNAPSHOT"
//val dottyVersion = "3.0.1-RC2"
val dottyVersion = "3.0.2"
val dottyVersion = "3.1.0-RC2"

ThisBuild/version := "0.9.4-SNAPSHOT"
ThisBuild/versionScheme := Some("semver-spec")
Expand Down Expand Up @@ -36,8 +36,10 @@ 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", "-Ycheck:macros", "-Yprint-syms",
"-Ydebug-tree-with-id", "1187431" ),
// -Ydebug-error
// -Ydebug-tree-with-id -1
Compile / doc / scalacOptions := Seq("-groups",
"-source-links:shared=github://rssh/dotty-cps-async/master#shared",
"-source-links:jvm=github://rssh/dotty-cps-async/master#jvm"),
Expand Down
4 changes: 4 additions & 0 deletions shared/src/main/scala/cps/Async.scala
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,7 @@ inline def async[F[_]](using inline am: CpsMonad[F]): macros.Async.InferAsyncArg
new macros.Async.InferAsyncArg[F]


// bug in dotty ?
//transparent inline def async[F[_]](using inline am: CpsContextMonad[F]) =
// new macros.Async.InferAsyncContextArg(using am)

8 changes: 8 additions & 0 deletions shared/src/main/scala/cps/CpsMonad.scala
Original file line number Diff line number Diff line change
Expand Up @@ -339,3 +339,11 @@ trait CpsSchedulingMonad[F[_]] extends CpsConcurrentMonad[F] {

}

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

type Context

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


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

trait CpsMonadContextProvider[F[_]]:

type Context

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


64 changes: 59 additions & 5 deletions shared/src/main/scala/cps/macros/Async.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,38 @@ import cps.macros.observatory.*

object Async {

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

transparent inline def apply[T](inline expr: T) =
transform[F,T](expr)(using am)
transform[F,T](using am)(expr)

// ?=> cause compilre crash.
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]

transparent inline def transform[F[_], T](inline expr: T)(using m: CpsMonad[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] =
${
Async.transformContextLambdaImpl[F,T,C]('expr)
}

/**
* transform expression and get monad from context.
**/
Expand All @@ -46,10 +63,10 @@ object Async {
val msg = s"Can't find async monad for ${TypeRepr.of[F].show} (transformImpl)"
report.throwError(msg, f)


/**
* transform expression within given monad. Use this function is you need to force async-transform
* from other macros
* from other macros without context
**/
def transformMonad[F[_]:Type,T:Type](f: Expr[T], dm: Expr[CpsMonad[F]])(using Quotes): Expr[F[T]] =
import quotes.reflect._
Expand Down Expand Up @@ -102,6 +119,7 @@ object Async {
report.throwError(ex.msg, ex.posExpr)



def adoptFlags[F[_]:Type,T](f: Expr[T], dm: Expr[CpsMonad[F]])(using Quotes): AsyncMacroFlags =
import quotes.reflect._
/*
Expand Down Expand Up @@ -231,4 +249,40 @@ 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]] =
import quotes.reflect._

def inInlined(t: Term, f: Term => Term): Term =
t match
case Inlined(call, bindings, body) => Inlined(call, bindings, f(body))
case other => other

def extractLambda(f:Term): (List[ValDef], Term, Term => Term ) =
f match
case Inlined(call, bindings, body) =>
val inner = extractLambda(body)
(inner._1, inner._2, t => Inlined(call, bindings, t) )
case Lambda(params,body) =>
params match
case List(vd) => (params, body, identity)
case _ => report.throwError(s"lambda with one argument expected, we have ${params}",cexpr)
case Block(Nil,nested@Lambda(params,body)) => extractLambda(nested)
case _ =>
report.throwError(s"lambda expected, have: ${f}", cexpr)

def transformNotInlined(t: Term): Term =
val (oldParams, body, nestFun) = extractLambda(t)
val transformed = transformImpl[F,T](body.asExprOf[T])
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) => {
TransformUtil.substituteLambdaParams( oldParams, params, transformed.asTerm, owner ).changeOwner(owner)
})
nestFun(nLambda)

val retval = inInlined(cexpr.asTerm, transformNotInlined).asExprOf[C => F[T]]
retval



}
5 changes: 0 additions & 5 deletions shared/src/test/scala/cps/TestCBS1If.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,6 @@ import cps.macros._

class TestBS1If:

@Test def tIfC1_000(): Unit =
val c = async[ComputationBound]{
if (true) 1 else 2
}
assert(c.run() == Success(1))

@Test def tIfC1_001(): Unit =
val c = Async.transform[ComputationBound,Int]{
Expand Down
85 changes: 85 additions & 0 deletions shared/src/test/scala/cps/context/TestDeferredInContext.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package cps.context

import scala.util.*
import scala.util.control.*

import cps.*

import java.util.concurrent.ConcurrentLinkedDeque

import org.junit.{Test,Ignore}
import org.junit.Assert._



class DeferredDestructorsContext {

val deferred: ConcurrentLinkedDeque[()=>Unit] = new ConcurrentLinkedDeque()

def deferr(f: =>Unit): Unit =
deferred.addFirst(() => f)

def cleanup(): Option[Throwable] =
{
var errors: Seq[Throwable] = Seq.empty
while(!deferred.isEmpty) {
val f = deferred.poll()
try {
f()
}catch{
case NonFatal(ex) =>
errors = errors :+ ex
}
}
if errors.isEmpty then
None
else
val first=errors.head
for(e <- errors.tail) {
first.addSuppressed(e)
}
Some(first)
}

def withContext[A](f: ComputationBound[A], m: CpsTryMonad[ComputationBound]): ComputationBound[A] =
m.flatMapTry(f){ r =>
r match
case Success(x) =>
cleanup() match
case None => m.pure(x)
case Some(ex) => m.error(ex)
case Failure(ex) =>
cleanup().foreach(ex.addSuppressed(_))
m.error(ex)
}

}

given DeferredDestructorsContextProvider: CpsMonadContextProvider[ComputationBound] with {

type Context = DeferredDestructorsContext

def contextualize[T](f: Context => ComputationBound[T] ): ComputationBound[T] =
val ctx = new DeferredDestructorsContext()
ctx.withContext(f(ctx), summon[CpsTryMonad[ComputationBound]])

}


class TestDeferredInContextMonad:



///*
//Compiler crash in dotty-3.1.0-RC1
//TODO: minimize
@Test def testSimpleContext(): Unit =
var x = 0
val c = async.in{ scope =>
scope.deferr{ x = 1 }
1
}
val r = c.run()
assert (x == 1)
assert(r == Success(1))
//*/

0 comments on commit 2f744f2

Please sign in to comment.