Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions sample/src-jvm/spores/sample/AutoCaptureExample.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import spores.conversions.given

object AutoCaptureExample {

// The `Spore.auto` method does the following:
// The `Spore(*)` method does the following:
//
// 1. Lifts all captured symbols to parameters
// 2. Find the implicit readwriters for each captured symbol
Expand All @@ -20,7 +20,7 @@ object AutoCaptureExample {
//
// {{{
// def foo(x: Int, y: Int) = {
// Spore.auto{ (i: Int) => x + y + i }
// Spore(*){ (i: Int) => x + y + i }
// }
// }}}
//
Expand All @@ -43,7 +43,7 @@ object AutoCaptureExample {
// A factory for a serialized function that checks if a number is between the
// numbers `x` and `y`.
def isBetween(x: Int, y: Int): Spore[Int => Boolean] = {
Spore.auto { (i: Int) => x <= i && i < y }
Spore(*) { (i: Int) => x <= i && i < y }
}


Expand All @@ -58,9 +58,9 @@ object AutoCaptureExample {

// Now we can create a similar factory but by capturing a `Range` object.
def isInRange(range: Range): Spore[Int => Boolean] = {
// The `Spore.auto` method will automatically pack the `Range` object and
// The `Spore(*)` method will automatically pack the `Range` object and
// its readwriter.
Spore.auto{ (i: Int) => range.x <= i && i < range.y }
Spore(*){ (i: Int) => range.x <= i && i < range.y }
}


Expand All @@ -69,7 +69,7 @@ object AutoCaptureExample {
// // no implicit values were found that match type spores.Spore[upickle.default.ReadWriter[spores.experimental.example.AutoCaptureExample.Range2]]
// case class Range2(x: Int, y: Int)
// def isInRange2(range: Range2): Spore[Int => Boolean] = {
// Spore.auto{ (i: Int) => range.x <= i && i < range.y }
// Spore(*){ (i: Int) => range.x <= i && i < range.y }
// }


Expand All @@ -88,7 +88,7 @@ object AutoCaptureExample {
assert(btwn1020.get().apply(25) == false)
println(btwn1020.apply(25))

val filter = Spore.auto { (l: List[Int]) => l.filter(btwn1020.get()) }
val filter = Spore(*) { (l: List[Int]) => l.filter(btwn1020.get()) }

println(filter)
// result: PackedWithEnv(PackedLambda(spores.experimental.example.AutoCaptureExample$Lambda$3),PackedEnv({"$type":"spores.Packed.PackedWithEnv","packed":{"$type":"spores.Packed.PackedWithEnv","packed":{"$type":"spores.Packed.PackedLambda","className":"spores.experimental.example.AutoCaptureExample$Lambda$1"},"packedEnv":{"$type":"spores.Packed.PackedEnv","env":"10","rw":{"$type":"spores.Packed.PackedObject","className":"spores.ReadWriters$IntRW$"}}},"packedEnv":{"$type":"spores.Packed.PackedEnv","env":"20","rw":{"$type":"spores.Packed.PackedObject","className":"spores.ReadWriters$IntRW$"}}},PackedObject(spores.ReadWriters$SporeRW$)))
Expand Down
6 changes: 3 additions & 3 deletions sample/src-jvm/spores/sample/ForComprehension.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ object ForComprehension {
private[Container] val sporeOpt: Option[Spore[A]]

inline def map[B](inline f: A => B): Container[B] = {
Container.create(this.sporeOpt.map(x => Spore.auto(f).withEnv2(x)))
Container.create(this.sporeOpt.map(x => Spore(*)(f).withEnv2(x)))
}

inline def flatMap[B](inline f: A => Container[B]): Container[B] = {
this.sporeOpt.map(x => Spore.auto(f).withEnv2(x).get()).getOrElse(Container.empty)
this.sporeOpt.map(x => Spore(*)(f).withEnv2(x).get()).getOrElse(Container.empty)
}

def withFilter(f: A => Boolean): Container[A] = {
Expand All @@ -29,7 +29,7 @@ object ForComprehension {
private[Container] override val sporeOpt: Option[Spore[A]] = so
}

inline def apply[A](inline a: A): Container[A] = create(Some(Spore.auto(a)))
inline def apply[A](inline a: A): Container[A] = create(Some(Spore(*)(a)))
def empty[A]: Container[A] = create(None)
}

Expand Down
16 changes: 8 additions & 8 deletions sample/src-jvm/spores/sample/SporeExample.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,26 @@ import spores.sample.platform.*

object SporeExample {

val Lambda1 = Spore.apply[Int => String] { x => x.toString.reverse }
val Lambda1 = Spore.apply[Int => String]() { x => x.toString.reverse }

val Lambda2 = Spore.applyWithEnv[Int, Int => String](12) { env => x => (env + x).toString.reverse }
val Lambda2 = Spore.apply[Int => Int => String]() { env => x => (env + x).toString.reverse }.withEnv(12)

val Lambda3 = Spore.apply[Option[Int] => Int] { x => x.map { _ + 1 }.getOrElse(0) }
val Lambda3 = Spore.apply[Option[Int] => Int]() { x => x.map { _ + 1 }.getOrElse(0) }

val Lambda4 = Spore.applyWithCtx[Int, Int](14) { summon[Int] }
val Lambda4 = Spore.apply[Int ?=> Int]() { summon[Int] }.withCtx(14)

// // Should cause compile error
// object ShouldFail:
// Spore.apply[Int => Int] { x =>
// Spore.apply[Int => Int] { y =>
// // Invalid capture of variable `x`. Use the first parameter of a spore's body to refer to the spore's environment.
// Spore.apply[Int => Int]() { x =>
// Spore.apply[Int => Int]() { y =>
// // Invalid capture of variable `x`. Add it to the capture list or use `*` to capture all by default.
// x + y
// }.get().apply(x)
// }

// // Should cause compile error
// import upickle.default.*
// def SporeFactoryFail[T: ReadWriter] = Spore.apply { summon[ReadWriter[T]] } // Invalid capture of variable `evidence$1`. Use the first parameter of a spore's body to refer to the spore's environment.
// def SporeFactoryFail[T: ReadWriter] = Spore.apply() { summon[ReadWriter[T]] } // Invalid capture of variable `evidence$1`. Add it to the capture list or use `*` to capture all by default.


def main(args: Array[String]): Unit = {
Expand Down
6 changes: 3 additions & 3 deletions sample/src-jvm/spores/sample/Workflow.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ object WorkflowMain {
// Custom error type for our workflow
case class Error(message: String) derives ReadWriter
// Custom Spore[ReadWriter[Error]] for our error type
given Spore[ReadWriter[Error]] = Spore.auto(summon)
given Spore[ReadWriter[Error]] = Spore(*)(summon)


def main(args: Array[String]): Unit = {
Expand Down Expand Up @@ -89,7 +89,7 @@ sealed trait Workflow[-Inp, +Out, Err] {
import Workflow.*

inline def map[OutB](inline f: Out => Either[Err, OutB])(using rw: Spore[ReadWriter[Either[Err, OutB]]]): Workflow[Inp, OutB, Err] = {
val spore = Spore.auto(f)
val spore = Spore(*)(f)
Map(this, spore, rw)
}
}
Expand Down Expand Up @@ -125,7 +125,7 @@ object Workflow {

/** Create a workflow starting from the initial `value`. */
def apply[Out, Err](value: Either[Err, Out])(using Spore[ReadWriter[Out]], Spore[ReadWriter[Err]]): Workflow[Nothing, Out, Err] = {
val spore = Spore.auto(value)
val spore = Spore(*)(value)
Value(spore)
}

Expand Down
117 changes: 25 additions & 92 deletions spores3/src-jvm/spores/SporeObjectCompanion.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package spores

import scala.annotation.targetName
import upickle.default.ReadWriter

import spores.*
Expand All @@ -13,112 +12,46 @@ private[spores] trait SporeObjectCompanion {
// This is a hack for having platform-specific operations in the companion
// object.

/** Create a Spore from the provided closure `body`.
*
* The created Spore is safe to serialize and deserialize. The closure must
* not capture any variables, otherwise it will cause a compile error.
*
* To capture variables, use [[applyWithEnv]], [[applyWithCtx]], or
* [[auto]] instead.
*
* @example
* {{{
* val mySpore = Spore.apply[Int => String] { x => x.toString.reverse }
* }}}
*
* @param body
* The closure.
* @tparam T
* The type of the closure.
* @return
* A new `Spore[T]` with the packed closure `body`.
*/
inline def apply[T](inline body: T): Spore[T] = {
SporeJVM.apply(body)
}

/** Create a Spore from the provided closure `body` with an environment
* variable `env` as the first parameter of the closure.
/** Create a Spore from the provided closure `body` and parameter list of
* captured variables `captures`.
*
* The created Spore is safe to serialize and deserialize. The closure must
* not capture any variables, otherwise it will cause a compile error.
* Captured variables in `body` must either be added to the `captures`
* parameter list, or the "capture all" mode `*` must be used. Mentioned
* variables in the `captures` list must be used in the `body`. For each
* capture of type `E` an implicit/given `Spore[ReadWriter[E]]` must be in
* contextual scope. Deviations will cause a compiler error. The created
* Spore is safe to serialize and deserialize.
*
* @example
* @example A Spore with no captures
* {{{
* val mySpore = Spore.applyWithEnv[Int, String](11) { env => (env + 12).toString.reverse }
* Spore.apply() { (x: Int) => x + 1 }
* }}}
*
* @param env
* The environment variable applied to the closure.
* @param body
* The closure.
* @param ev
* The implicit `Spore[ReadWriter[E]]` used for packing the `env`.
* @tparam E
* The type of the environment variable.
* @tparam T
* The return type of the closure.
* @return
* A new `Spore[T]` with the packed closure `body` applied to the `env`.
*/
inline def applyWithEnv[E, T](inline env: E)(inline body: E => T)(using ev: Spore[ReadWriter[E]]): Spore[T] = {
SporeJVM.applyWithEnv(env)(body)
}

/** Create a Spore from the provided closure `body` with an environment
* variable `env` as the first **implicit** parameter of the closure.
*
* The created Spore is safe to serialize and deserialize. The closure must
* not capture any variables, otherwise it will cause a compile error.
*
* @example
* @example A Spore with explicit captures
* {{{
* val mySpore = Spore.applyWithCtx[Int, String](11) { env ?=> (env + 12).toString.reverse }
* val y = 12
* val z = 13
* Spore.apply(y, z) { (x: Int) => x + y + z + 1 }
* }}}
*
* @param env
* The context environment variable applied to the closure.
* @param body
* The closure.
* @param ev
* The implicit `Spore[ReadWriter[E]]` used for packing the `env`.
* @tparam E
* The type of the context environment variable.
* @tparam T
* The return type of the closure.
* @return
* A new `Spore[T]` with the packed closure `body` using the implicit `env`.
*/
inline def applyWithCtx[E, T](inline env: E)(inline body: E ?=> T)(using ev: Spore[ReadWriter[E]]): Spore[T] = {
SporeJVM.applyWithCtx(env)(body)
}

/** Create a Spore from `f`. Automatically captures variables and checks
* captured variables. Captured variables in `f` must have an implicit
* `Spore[ReadWriter[T]]` in scope, where `T` is the type of the captured
* variable.
*
* The created Spore is safe to serialize and deserialize. If a captured
* variable does not have an implicit `Spore[ReadWriter[T]]` in scope then it
* will cause a compile error.
*
* @example
* @example A Spore with `*` capture all mode
* {{{
* def isBetween(x: Int , y: Int): Spore[Int => Boolean] = {
* // `x` and `y` are captured variables of type `Int`, so we need to
* // provide an implicit `Spore[ReadWriter[Int]]` in scope.
* Spore.auto { (i: Int) => x <= i && i < y }
* }
* val y = 12
* val z = 13
* Spore.apply(*) { (x: Int) => x + y + z + 1 }
* }}}
*
* @param captures
* The captured variables used in the `body`, or `*` to capture all by
* default.
* @param body
* The closure.
* @tparam T
* The type of the closure.
* @return
* A new `Spore[F]` with the packed closure `f`.
* A new `Spore[T]`.
*/
inline def auto[T](inline body: T): Spore[T] = {
SporeJVM.auto(body)
inline def apply[T](inline captures: Any*)(inline body: T): Spore[T] = {
SporeJVM0.apply[ReadWriter, T](captures*)(body)
}

}
61 changes: 32 additions & 29 deletions spores3/src-jvm/spores/SporeObjectCompanion0.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,49 +6,52 @@ import spores.jvm.*

/** Internal API. Extended from by the [[spores.Spore0]] companion object. */
private[spores] trait SporeObjectCompanion0 {

// This is a hack for having platform-specific operations in the companion
// object.

inline def apply[F[_], T](inline body: T): Spore0[F, T] = {
SporeJVM0.apply(body)
}

inline def applyWithEnv[F[_], E, T](inline env: E)(inline body: E => T)(using ev: Spore0[F, F[E]]): Spore0[F, T] = {
apply[F, E => T](body).withEnv(env)(using ev)
}

inline def applyWithCtx[F[_], E, T](inline env: E)(inline body: E ?=> T)(using ev: Spore0[F, F[E]]): Spore0[F, T] = {
apply[F, E ?=> T](body).withCtx(env)(using ev)
}

/** Create a Spore0 from `body`. Automatically captures variables and checks
* captured variables. Captured variables in `body` must have an implicit
* `Spore[F[E]]` in scope, where `E` is the type of the captured environment
* variable and `F[_]` is the type of the captured evidence.
/** Create a Spore0 from the provided closure `body` and parameter list of
* captured variables `captures` with evidence type `F[_]`.
*
* If a captured
* variable does not have an implicit `Spore[F[E]]` in scope then it
* will cause a compile error.
* Captured variables in `body` must either be added to the `captures`
* parameter list, or the "capture all" mode `*` must be used. Mentioned
* variables in the `captures` list must be used in the `body`. For each
* capture of type `E` an implicit/given `Spore0[F, F[E]]` must be in
* contextual scope. Deviations will cause a compiler error.
*
* @example
* @example A Spore with no captures
* {{{
* def isBetween(x: Int , y: Int): Spore0[F, Int => Boolean] = {
* // `x` and `y` are captured variables of type `Int`, so we need to
* // provide an implicit `Spore[F[Int]]` in scope.
* Spore0.auto { (i: Int) => x <= i && i < y }
* }
* Spore0.apply[F, Int => Int]() { (x: Int) => x + 1 }
* }}}
* @example A Spore with explicit captures
* {{{
* val y = 12
* val z = 13
* Spore0.apply[F, Int => Int](y, z) { (x: Int) => x + y + z + 1 }
* }}}
* @example A Spore with `*` capture all mode
* {{{
* val y = 12
* val z = 13
* Spore0.apply[F, Int => Int](*) { (x: Int) => x + y + z + 1 }
* }}}
*
* @param captures
* The captured variables used in the `body`, or `*` to capture all by
* default.
* @param body
* The closure.
* @tparam F
* The evidence type for the captures. Each capture of type `E` requires an
* implicit/given `Spore0[F, F[E]]` in contextual scope.
* @tparam T
* The type of the closure.
* @tparam F
* The type of the captured evidence.
* @return
* A new `Spore[F]` with the packed closure `f`.
* A new `Spore0[F, T]`.
*/
inline def auto[F[_], T](inline body: T): Spore0[F, T] = {
SporeJVM0.auto(body)
inline def apply[F[_], T](inline captures: Any*)(inline body: T): Spore0[F, T] = {
SporeJVM0.apply[F, T](captures*)(body)
}

}
2 changes: 1 addition & 1 deletion spores3/src/spores/Duplicable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ object Duplicable {
duplicable.duplicate(value)

inline def apply[T](inline body: T): Spore0[Duplicable, T] = {
spores.jvm.SporeJVM0.apply(body)
spores.jvm.SporeJVM0.apply()(body)
}

inline def applyWithEnv[E, T](env: E)(inline body: E => T)(using ev: Spore0[Duplicable, Duplicable[E]]): Spore0[Duplicable, T] = {
Expand Down
Loading
Loading