Skip to content

Commit

Permalink
finally resurrected Traverse, resolves LoyolaChicagoCode#26
Browse files Browse the repository at this point in the history
Signed-off-by: Konstantin Läufer <[email protected]>
  • Loading branch information
klaeufer committed Dec 15, 2023
1 parent 3759185 commit a849350
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 40 deletions.
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name := "expressions-algebraic-scala"

version := "0.4"

scalaVersion := "3.3.0"
scalaVersion := "3.3.1"

scalacOptions += "@.scalacOptions.txt"

Expand Down
66 changes: 28 additions & 38 deletions src/main/scala/structures.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package edu.luc.cs.cs371.expressionsAlgebraic

import cats.{Applicative, Eq, Eval, Functor, Show, Traverse}
import cats.{Applicative, Eq, Show, Traverse}
import higherkindness.droste.data.Fix
import higherkindness.droste.util.DefaultTraverse

/**
* In this example, we represent arithmetic expressions as trees
Expand Down Expand Up @@ -36,47 +37,36 @@ object structures:
* instance below, which defines a further generalization
* of `map`.
*/
given Functor[ExprF] = new Functor[ExprF]:
override def map[A, B](fa: ExprF[A])(f: A => B): ExprF[B] = fa match
case c @ Constant(v) => c
case UMinus(r) => UMinus(f(r))
case Plus(l, r) => Plus(f(l), f(r))
case Minus(l, r) => Minus(f(l), f(r))
case Times(l, r) => Times(f(l), f(r))
case Div(l, r) => Div(f(l), f(r))
case Mod(l, r) => Mod(f(l), f(r))
// given Functor[ExprF] = new Functor[ExprF]:
// override def map[A, B](fa: ExprF[A])(f: A => B): ExprF[B] = fa match
// case c @ Constant(v) => c
// case UMinus(r) => UMinus(f(r))
// case Plus(l, r) => Plus(f(l), f(r))
// case Minus(l, r) => Minus(f(l), f(r))
// case Times(l, r) => Times(f(l), f(r))
// case Div(l, r) => Div(f(l), f(r))
// case Mod(l, r) => Mod(f(l), f(r))
// end exprFFunctor

given [T](using Eq[T]): Eq[ExprF[T]] = Eq.fromUniversalEquals

given [T](using Show[T]): Show[ExprF[T]] = Show.fromToString

// TODO bring this back

/**
* Implicit value for declaring `ExprF` as an instance of
* typeclass `Traverse` in Cats. This requires us to define
* `traverse`.
*/
// given Traverse[ExprF] = new Traverse[ExprF]:
// import cats.implicits.*
// override def traverse[G[_]: Applicative, A, B](fa: ExprF[A])(f: A => G[B]): G[ExprF[B]] = fa match
// case c @ Constant(v) => c.pure[G]
// case UMinus(r) => f(r).map(UMinus(_))
// case Plus(l, r) => (f(l), f(r)).mapN(Plus(_, _))
// case Minus(l, r) => (f(l), f(r)).mapN(Minus(_, _))
// case Times(l, r) => (f(l), f(r)).mapN(Times(_, _))
// case Div(l, r) => (f(l), f(r)).mapN(Div(_, _))
// case Mod(l, r) => (f(l), f(r)).mapN(Mod(_, _))
// // TODO working implementations of foldRight and foldLeft
// // problem: need Monoid or Applicative to implement foldRight as foldMap or traverse
// def foldRight[A, B](fa: ExprF[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = ???
// def foldLeft[A, B](fa: ExprF[A], b: B)(f: (B, A) => B): B = ???
// // override def foldMap[A, B](fa: ExprF[A])(f: A => B)(implicit B: Monoid[B]): B =
// // traverse[Const[B, *], A, B](fa)(a => Const(f(a))).getConst
// // foldMap(fa)(f(_, lb))
// // import cats.data.Const
// // traverse[Const[Eval[B], *], A, Eval[B]](fa)(a => Const(f(a, lb))).getConst
given Traverse[ExprF] = new DefaultTraverse[ExprF]:
import cats.syntax.all.*
override def traverse[G[_]: Applicative, A, B](fa: ExprF[A])(f: A => G[B]): G[ExprF[B]] = fa match
case c @ Constant(v) => c.pure[G]
case UMinus(r) => f(r).map(UMinus(_))
case Plus(l, r) => (f(l), f(r)).mapN(Plus(_, _))
case Minus(l, r) => (f(l), f(r)).mapN(Minus(_, _))
case Times(l, r) => (f(l), f(r)).mapN(Times(_, _))
case Div(l, r) => (f(l), f(r)).mapN(Div(_, _))
case Mod(l, r) => (f(l), f(r)).mapN(Mod(_, _))

/** Least fixpoint of `ExprF` as carrier object for the initial algebra. */
type Expr = Fix[ExprF]
Expand All @@ -86,13 +76,13 @@ object structures:

/** Factory for creating Expr instances. */
object Expr:
def constant(c: Int) = Fix(Constant(c))
def uminus(r: Expr) = Fix(UMinus(r))
def plus(l: Expr, r: Expr) = Fix(Plus(l, r))
def minus(l: Expr, r: Expr) = Fix(Minus(l, r))
def times(l: Expr, r: Expr) = Fix(Times(l, r))
def div(l: Expr, r: Expr) = Fix(Div(l, r))
def mod(l: Expr, r: Expr) = Fix(Mod(l, r))
def constant(c: Int): Expr = Fix(Constant(c))
def uminus(r: Expr): Expr = Fix(UMinus(r))
def plus(l: Expr, r: Expr): Expr = Fix(Plus(l, r))
def minus(l: Expr, r: Expr): Expr = Fix(Minus(l, r))
def times(l: Expr, r: Expr): Expr = Fix(Times(l, r))
def div(l: Expr, r: Expr): Expr = Fix(Div(l, r))
def mod(l: Expr, r: Expr): Expr = Fix(Mod(l, r))
end Expr

given Eq[Expr] = Eq.fromUniversalEquals
Expand Down
2 changes: 1 addition & 1 deletion src/test/scala/lawTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,6 @@ object lawTests extends Properties("lawTests"):
//include(equal.laws[Expr], "equalExpr.")

include(cats.laws.discipline.FunctorTests[ExprF].functor[Int, Int, Int].all)
// include(cats.laws.discipline.TraverseTests[ExprF].traverse[Int, Int, Int, Int, Option, Option].all)
include(cats.laws.discipline.TraverseTests[ExprF].traverse[Int, Int, Int, Int, Option, Option].all)

end lawTests

0 comments on commit a849350

Please sign in to comment.