Skip to content

Commit

Permalink
experimental case class support...
Browse files Browse the repository at this point in the history
  • Loading branch information
kitlangton committed Mar 7, 2024
1 parent 29e1b94 commit f570a0b
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 3 deletions.
12 changes: 12 additions & 0 deletions examples/src/main/scala/examples/types/Newtypes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,15 @@ type PositiveIntList = PositiveIntList.Type
object PositiveIntList extends Newtype[List[Int]]:
override inline def validate(value: List[Int]): Boolean =
!value.exists(_ < 0)

// It sort of works with case classes...
case class Person(name: String, age: Int)

type ElderlyPerson = ElderlyPerson.Type
object ElderlyPerson extends Newtype[Person]:
override inline def validate(value: Person): Boolean =
value.age > 65

object NewtypeExamples:
val elder = ElderlyPerson(Person("Lazarus", 70))
// val youth = ElderlyPerson(Person("Kit", 30))
42 changes: 42 additions & 0 deletions modules/core/shared/src/main/scala/neotype/eval/Eval.scala
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,16 @@ sealed trait Eval[A]:
val thenEvalStr = thenEval.render
val elseEvalStr = elseEval.render
s"if $condStr then $thenEvalStr else $elseEvalStr"

case ProductValue(name, fields) =>
s"$name(${fields
.map { (k, v) =>
s"$k = ${v.render}"
}
.mkString(", ")})"

case ProductSelect(eval, field) =>
s"${eval.render}.$field"
end render

private def indent(str: String): String =
Expand Down Expand Up @@ -124,6 +134,14 @@ sealed trait Eval[A]:
case EvalConstruct(constructor, args) =>
constructor(args.map(_.result))

case ProductValue(name, fields) =>
fields.view.mapValues(_.result).toMap

case ProductSelect(eval, field) =>
println(s"SELECTING $field FROM $eval")
println(s"RESULT: ${eval.result}")
eval.result.asInstanceOf[Map[String, Any]].getOrElse(field, throw MatchError(field))

case EvalApply(eval, args) =>
val calcResult = eval.result
calcResult match
Expand Down Expand Up @@ -172,6 +190,9 @@ object Eval:
case class EvalConstruct[A](constructor: List[Any] => A, args: List[Eval[?]]) extends Eval[A]
case class EvalApply[A](eval: Eval[A], args: List[Eval[?]]) extends Eval[Any]

case class ProductValue(name: String, fields: Map[String, Eval[?]]) extends Eval[Any]
case class ProductSelect[A](eval: Eval[A], field: String) extends Eval[Any]

case class EvalStringContext[A](parts: List[String], args: List[Eval[?]]) extends Eval[String]

case class EvalBlock[A](defs: List[EvalDef[?]], eval: Eval[A]) extends Eval[A]
Expand Down Expand Up @@ -235,6 +256,17 @@ object Eval:
case '{ BigDecimal(${ Expr(double) }: Double) } => Some(Eval.Value(BigDecimal(double)))
case '{ (${ Expr(string) }: String).r } => Some(Eval.Value(string.r))

case Unseal(p @ Apply(Select(_, "apply"), Evals(args))) if p.tpe.typeSymbol.flags.is(Flags.Case) =>
val typeName = p.tpe.typeSymbol.name
val fieldNames = p.tpe.typeSymbol.primaryConstructor.paramSymss.flatten.map(_.name)
val fields = fieldNames.zip(args).toMap
val product = ProductValue(typeName, fields)
Some(product)

case Unseal(Select(p @ Seal(Eval(eval)), field)) if p.tpe.typeSymbol.flags.is(Flags.Case) =>
val productSelect = ProductSelect(eval, field)
Some(productSelect)

// Implicit Conversions
case '{ int2Integer(${ Eval(int) }: Int) } => Some(int)

Expand Down Expand Up @@ -851,6 +883,16 @@ object Evals:
case None => return None
Some(builder.result())

def unapply(using Quotes)(terms: Seq[quotes.reflect.Term]): Option[Seq[Eval[?]]] =
val builder = Seq.newBuilder[Eval[?]]
val iter = terms.iterator
while iter.hasNext do
val term = iter.next()
Eval.unapply(term.asExpr) match
case Some(eval) => builder += eval
case None => return None
Some(builder.result())

end Evals

object MatchOrdering:
Expand Down
13 changes: 10 additions & 3 deletions modules/core/shared/src/test/scala/neotype/eval/EvalSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -277,12 +277,16 @@ val evalTests =
} -> 11,
eval {
List(0, 1, 2, 3).map(_ * 2)
} -> List(0, 2, 4, 6),
} -> List(0, 2, 4, 6),
eval(List(0, 1, 2, 3).filter(a => a > 1)) -> List(2, 3),
eval {
List(0, 1, 2, 3).filter(a => a > 1)
} -> List(2, 3)
val x = Person("Hello", 12)
x.name
} -> "Hello"
)

case class Person(name: String, age: Int)

object IterableTest:
eval {
val iterable = List(1, 2, 3)
Expand Down Expand Up @@ -378,4 +382,7 @@ object IterableTest:
// iterable.zipWithIndex

0 // TODO: Extend FromExpr to work with Some(1), (), etc.

// Case Classes

}

0 comments on commit f570a0b

Please sign in to comment.