Skip to content
Merged
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
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ lazy val ducktape =
.enablePlugins(TypelevelMimaPlugin)
.in(file("ducktape"))
.settings(
scalacOptions ++= List("-deprecation", "-Wunused:all"),
scalacOptions ++= List("-deprecation", "-Wunused:all", "-Ykind-projector:underscores"),
Test / scalacOptions --= List("-deprecation"),
Test / scalacOptions ++= List("-Werror", "-Wconf:cat=deprecation:s"),
libraryDependencies += "org.scalameta" %%% "munit" % "1.0.0" % Test
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
package io.github.arainko.ducktape.internal

sealed trait Context {
private[ducktape] sealed trait Context {
type F <: Fallible

def summoner: Summoner[F]
def transformationSite: TransformationSite

final def reify[FF <: Fallible, G[x]](value: G[FF])(using ev: G[FF] =:= G[F]): G[F] = ev(value)
final def reifyPlan[FF <: Fallible](value: Plan[Erroneous, F])(using ev: Plan[Erroneous, F] =:= Plan[Erroneous, FF]) =
ev(value)

final def toTotal: Context.Total = Context.Total(transformationSite)
}

object Context {
private[ducktape] object Context {
type Of[F0 <: Fallible] = Context { type F = F0 }

inline def current(using ctx: Context): ctx.type = ctx
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ private[ducktape] object FalliblePlanInterpreter {
plan.dest.tpe match {
case '[dest] =>
F match {
case TransformationMode.Accumulating(f) =>
case TransformationMode.Accumulating(f, _) =>
NonEmptyList
.fromList(wrapped.toList)
.map { wrappeds =>
Expand Down Expand Up @@ -303,7 +303,7 @@ private[ducktape] object FalliblePlanInterpreter {
plan.dest.tpe match {
case '[dest] =>
F match {
case TransformationMode.Accumulating(f) =>
case TransformationMode.Accumulating(f, _) =>
NonEmptyList
.fromList(wrapped.toList)
.map { wrappeds =>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
package io.github.arainko.ducktape.internal

import io.github.arainko.ducktape.Mode
import io.github.arainko.ducktape.internal.Context.{ PossiblyFallible, Total }
import io.github.arainko.ducktape.internal.Plan.{ Derived, UserDefined }
import io.github.arainko.ducktape.internal.Summoner.UserDefined.{ FallibleTransformer, TotalTransformer }
import io.github.arainko.ducktape.internal.*

import scala.annotation.unused
import scala.collection.immutable.VectorMap
import scala.quoted.*
import scala.util.boundary
Expand Down Expand Up @@ -263,10 +261,6 @@ private[ducktape] object Planner {
)(using Quotes, Depth, Context.Of[F]): Option[Plan[Erroneous, F]] =
PartialFunction.condOpt(Context.current *: structs) {
case (ctx: Context.PossiblyFallible[f], source @ Wrappped(tpe, path, underlying), dest) =>
// the compiler needs a bit more encouragement to be sure that the plan we construct has a fallibility of F
// Context.PossiblyFallible is defined with a type F = Fallible so we can deduce that ctx.F =:= Fallible =:= F
val evidence = summon[Plan[Erroneous, ctx.F] =:= Plan[Erroneous, F]]

// needed for the recurse call to return Plan[Erroneous, Nothing]
given Context.Total = ctx.toTotal
val plan = Plan.BetweenFallibleNonFallible(
Expand All @@ -275,7 +269,9 @@ private[ducktape] object Planner {
recurse(underlying, dest)
)

evidence(plan)
// the compiler needs a bit more encouragement to be sure that the plan we construct has a fallibility of F
// Context.PossiblyFallible is defined with a type F = Fallible so we can deduce that ctx.F =:= Fallible =:= F
ctx.reifyPlan[F](plan)
}

}
Expand All @@ -284,43 +280,40 @@ private[ducktape] object Planner {
def unapply[F <: Fallible](
structs: (Structure, Structure)
)(using Quotes, Depth, Context.Of[F]): Option[Plan[Erroneous, F]] =
(Context.current *: structs) match {
PartialFunction.condOpt(Context.current *: structs) {
case (
ctx @ Context.PossiblyFallible(_, _, _, mode: TransformationMode.FailFast[f]),
source @ Wrappped(tpe, path, underlying),
dest
) =>
val evidence = summon[Plan[Erroneous, ctx.F] =:= Plan[Erroneous, F]]

Some(
ctx.reifyPlan[F] {
Plan.BetweenFallibles(
source,
dest,
mode,
recurse(underlying, dest)
)
).map(evidence)
}

case (
ctx @ Context.PossiblyFallible(wrapperType, _, _, mode: TransformationMode.Accumulating[f]),
ctx @ Context.PossiblyFallible(
WrapperType.Wrapped(given Type[f]),
_,
_,
TransformationMode.Accumulating(mode, Some(localMode))
),
source @ Wrappped(tpe, path, underlying),
dest
) =>
val evidence = summon[Plan[Erroneous, ctx.F] =:= Plan[Erroneous, F]]
@unused given Type[f] = wrapperType.wrapperTpe
Expr
.summon[Mode.FailFast[f]] // summon a fallback FF mode to use for the transformation step
.map { mode =>
Plan.BetweenFallibles(
source,
dest,
TransformationMode.FailFast(mode),
recurse(underlying, dest)
)
}
.map(evidence)

case _ => None
ctx.reifyPlan[F] {
Plan.BetweenFallibles(
source,
dest,
TransformationMode.FailFast(localMode),
recurse(underlying, dest)
)
}

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ import scala.quoted.*
private[ducktape] enum TransformationMode[F[+x]] {
def value: Expr[Mode[F]]

case Accumulating(value: Expr[Mode.Accumulating[F]])
case Accumulating(value: Expr[Mode.Accumulating[F]], fallback: Option[Expr[Mode.FailFast[F]]])
case FailFast(value: Expr[Mode.FailFast[F]])
}

private[ducktape] object TransformationMode {
def create[F[+x]: Type](expr: Expr[Mode[F]])(using Quotes): TransformationMode[F] =
expr match
case '{ $acc: Mode.Accumulating[F] } =>
Accumulating(acc)
Accumulating(acc, Expr.summon[Mode.FailFast[F]])
case '{ $ff: Mode.FailFast[F] } =>
FailFast(ff)
case other =>
Expand All @@ -27,8 +27,8 @@ private[ducktape] object TransformationMode {
given Debug[TransformationMode[?]] with {
def astify(self: TransformationMode[?])(using Quotes): AST =
self match
case Accumulating(value) => AST.Text("Accumulating")
case FailFast(value) => AST.Text("FailFast")
case Accumulating(value, _) => AST.Text("Accumulating")
case FailFast(value) => AST.Text("FailFast")

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ package io.github.arainko.ducktape.internal
import scala.annotation.unused
import scala.quoted.*

sealed trait WrapperType {
private[ducktape] sealed trait WrapperType {
def unapply(tpe: Type[?])(using Quotes): Option[(WrapperType, Type[?])]
}

object WrapperType {
private[ducktape] object WrapperType {

def unapply(using Quotes, Context)(tpe: Type[?]) =
Context.current match {
Expand All @@ -19,7 +19,7 @@ object WrapperType {
override def unapply(tpe: Type[? <: AnyKind])(using Quotes): Option[(WrapperType, Type[?])] = None
}

final class Wrapped[F[+x]](val wrapperTpe: Type[F]) extends WrapperType {
final case class Wrapped[F[+x]](wrapperTpe: Type[F]) extends WrapperType {
override def unapply(tpe: Type[? <: AnyKind])(using Quotes): Option[(WrapperType, Type[?])] = {
@unused given Type[F] = wrapperTpe
tpe match
Expand Down