Skip to content

Commit

Permalink
Add support for deriveTraverse in scala3
Browse files Browse the repository at this point in the history
By shamelessly copying code from `kittens`
  • Loading branch information
Daenyth committed Jul 15, 2021
1 parent 2e2388f commit 6c377a3
Show file tree
Hide file tree
Showing 8 changed files with 286 additions and 1 deletion.
8 changes: 8 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,14 @@ lazy val macros = module("macros")
"org.scalamacros" %% "paradise" % "2.1.1" cross CrossVersion.patch
)
).value,
// For some reason dependencies using `%%%` don't work with our 'on' method.
// They give 'error: Illegal dynamic dependency'
libraryDependencies ++=
(CrossVersion.partialVersion(scalaVersion.value) match {
case Some((3, _)) =>
Seq("org.typelevel" %%% "shapeless3-deriving" % "3.0.1")
case _ => Nil
}),
scalacOptions ++= on(2, 13)("-Ymacro-annotations").value
)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package higherkindness.droste.derivation

import shapeless3.deriving.*
import scala.annotation.*
import scala.compiletime.*

/*
Credit to the `kittens` project. This is a direct copy/paste from there
https://github.com/typelevel/kittens/blob/5b3b7ca168b9636f340b23bc95a4fd1506a26707/core/src/main/scala-3/cats/derived/derived.scala
*/

opaque type Derived[A] = A
object Derived:
def apply[A](instance: A): Derived[A] = instance
extension [A](derived: Derived[A]) def instance: A = derived
given [A]: Conversion[A, Derived[A]] = apply

type Or0[F[_]] = [x] =>> Or[F[x]]
type Or1[F[_[_]]] = [x[_]] =>> Or[F[x]]
type Or11[F[_[_[_]]]] = [x[_[_]]] =>> Or[F[x]]
type Or2[F[_[_, _]]] = [x[_, _]] =>> Or[F[x]]

opaque type Or[A] = A
object Or extends OrInstances:
def apply[A](instance: A): Or[A] = instance
extension [A](or: Or[A]) def unify: A = or
extension [I[f[_], t] <: K0.Instances[f, t], F[_], T]
(inst: I[Or0[F], T]) @targetName("unifyK0") def unify: I[F, T] = inst
extension [I[f[_[_]], t[_]] <: K1.Instances[f, t], F[_[_]], T[_]]
(inst: I[Or1[F], T]) @targetName("unifyK1") def unify: I[F, T] = inst
extension [I[f[_[_[_]]], t[_[_]]] <: K11.Instances[f, t], F[_[_[_]]], T[_[_]]]
(inst: I[Or11[F], T]) @targetName("unifyK11") def unify: I[F, T] = inst
extension [I[f[_[_, _]], t[_, _]] <: K2.Instances[f, t], F[_[_, _]], T[_, _]]
(inst: I[Or2[F], T]) @targetName("unifyK2") def unify: I[F, T] = inst

sealed abstract class OrInstances:
inline given [A]: Derived.Or[A] = summonFrom {
case instance: A => Derived.Or(instance)
case derived: Derived[A] => Derived.Or(derived.instance)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package higherkindness.droste.derivation

import cats.{Eval, Foldable}
import shapeless3.deriving.{Const, Continue, K1}
import scala.compiletime.*

/*
Credit to the `kittens` project. This is a direct copy/paste from there
https://github.com/typelevel/kittens/blob/5b3b7ca168b9636f340b23bc95a4fd1506a26707/core/src/main/scala-3/cats/derived/foldable.scala
*/

type DerivedFoldable[F[_]] = Derived[Foldable[F]]
object DerivedFoldable:
type Or[F[_]] = Derived.Or[Foldable[F]]
inline def apply[F[_]]: Foldable[F] =
import DerivedFoldable.given
summonInline[DerivedFoldable[F]].instance

given [T]: DerivedFoldable[Const[T]] = new Foldable[Const[T]]:
def foldLeft[A, B](fa: T, b: B)(f: (B, A) => B): B = b
def foldRight[A, B](fa: T, lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = lb

given [F[_], G[_]](using F: Or[F], G: Or[G]): DerivedFoldable[[x] =>> F[G[x]]] =
F.unify `compose` G.unify

given [F[_]](using inst: K1.ProductInstances[Or, F]): DerivedFoldable[F] =
new Product(using inst.unify) {}

given [F[_]](using inst: => K1.CoproductInstances[Or, F]): DerivedFoldable[F] =
new Coproduct(using inst.unify) {}

trait Product[T[x[_]] <: Foldable[x], F[_]](using inst: K1.ProductInstances[T, F])
extends Foldable[F]:

def foldLeft[A, B](fa: F[A], b: B)(f: (B, A) => B): B =
inst.foldLeft[A, B](fa)(b) { [f[_]] => (acc: B, tf: T[f], fa: f[A]) =>
Continue(tf.foldLeft(fa, acc)(f))
}

def foldRight[A, B](fa: F[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] =
inst.foldRight[A, Eval[B]](fa)(lb) { [f[_]] => (tf: T[f], fa: f[A], acc: Eval[B]) =>
Continue(Eval.defer(tf.foldRight(fa, acc)(f)))
}

trait Coproduct[T[x[_]] <: Foldable[x], F[_]](using inst: K1.CoproductInstances[T, F])
extends Foldable[F]:

def foldLeft[A, B](fa: F[A], b: B)(f: (B, A) => B): B =
inst.fold[A, B](fa) { [f[_]] => (tf: T[f], fa: f[A]) =>
tf.foldLeft(fa, b)(f)
}

def foldRight[A, B](fa: F[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] =
inst.fold[A, Eval[B]](fa) { [f[_]] => (tf: T[f], fa: f[A]) =>
Eval.defer(tf.foldRight(fa, lb)(f))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package higherkindness.droste.derivation

import cats.Functor
import shapeless3.deriving.{Const, K1}
import scala.compiletime.*

/*
Credit to the `kittens` project. This is a direct copy/paste from there
https://github.com/typelevel/kittens/blob/5b3b7ca168b9636f340b23bc95a4fd1506a26707/core/src/main/scala-3/cats/derived/functor.scala
*/

type DerivedFunctor[F[_]] = Derived[Functor[F]]
object DerivedFunctor:
type Or[F[_]] = Derived.Or[Functor[F]]
inline def apply[F[_]]: Functor[F] =
import DerivedFunctor.given
summonInline[DerivedFunctor[F]].instance

given [T]: DerivedFunctor[Const[T]] = new Functor[Const[T]]:
def map[A, B](fa: T)(f: A => B): T = fa

given [F[_], G[_]](using F: Or[F], G: Or[G]): DerivedFunctor[[x] =>> F[G[x]]] =
F.unify `compose` G.unify

given [F[_]](using inst: => K1.Instances[Or, F]): DerivedFunctor[F] =
new Generic(using inst.unify) {}

trait Generic[T[x[_]] <: Functor[x], F[_]](using inst: K1.Instances[T, F])
extends Functor[F]:

def map[A, B](fa: F[A])(f: A => B): F[B] =
inst.map(fa: F[A]) { [f[_]] => (tf: T[f], fa: f[A]) =>
tf.map(fa)(f)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package higherkindness.droste.derivation

import cats.{Applicative, Eval, Traverse}
import shapeless3.deriving.{Const, Continue, K1}
import scala.compiletime.*

/*
Credit to the `kittens` project. This is a direct copy/paste from there
https://github.com/typelevel/kittens/blob/5b3b7ca168b9636f340b23bc95a4fd1506a26707/core/src/main/scala-3/cats/derived/traverse.scala
*/


type DerivedTraverse[F[_]] = Derived[Traverse[F]]
object DerivedTraverse:
type Or[F[_]] = Derived.Or[Traverse[F]]
inline def apply[F[_]]: Traverse[F] =
import DerivedTraverse.given
summonInline[DerivedTraverse[F]].instance

given [T]: DerivedTraverse[Const[T]] = new Traverse[Const[T]]:
override def map[A, B](fa: T)(f: A => B): T = fa
def foldLeft[A, B](fa: T, b: B)(f: (B, A) => B): B = b
def foldRight[A, B](fa: T, lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = lb
def traverse[G[_], A, B](fa: T)(f: A => G[B])(using G: Applicative[G]): G[T] = G.pure(fa)

given [F[_], G[_]](using F: Or[F], G: Or[G]): DerivedTraverse[[x] =>> F[G[x]]] =
F.unify `compose` G.unify

given [F[_]](using inst: K1.ProductInstances[Or, F]): DerivedTraverse[F] =
given K1.ProductInstances[Traverse, F] = inst.unify
new Product[Traverse, F] {}

given [F[_]](using inst: => K1.CoproductInstances[Or, F]): DerivedTraverse[F] =
given K1.CoproductInstances[Traverse, F] = inst.unify
new Coproduct[Traverse, F] {}

trait Product[T[x[_]] <: Traverse[x], F[_]](using inst: K1.ProductInstances[T, F])
extends DerivedFunctor.Generic[T, F], DerivedFoldable.Product[T, F], Traverse[F]:

def traverse[G[_], A, B](fa: F[A])(f: A => G[B])(using G: Applicative[G]): G[F[B]] =
inst.traverse[A, G, B](fa) { [a, b] => (ga: G[a], f: a => b) =>
G.map(ga)(f)
} { [a] => (x: a) =>
G.pure(x)
} { [a, b] => (gf: G[a => b], ga: G[a]) =>
G.ap(gf)(ga)
} { [f[_]] => (tf: T[f], fa: f[A]) =>
tf.traverse(fa)(f)
}

trait Coproduct[T[x[_]] <: Traverse[x], F[_]](using inst: K1.CoproductInstances[T, F])
extends DerivedFunctor.Generic[T, F], DerivedFoldable.Coproduct[T, F], Traverse[F]:

def traverse[G[_], A, B](fa: F[A])(f: A => G[B])(using G: Applicative[G]): G[F[B]] =
inst.traverse[A, G, B](fa) { [a, b] => (ga: G[a], f: a => b) =>
G.map(ga)(f)
} { [a] => (x: a) =>
G.pure(x)
} { [a, b] => (gf: G[a => b], ga: G[a]) =>
G.ap(gf)(ga)
} { [f[_]] => (tf: T[f], fa: f[A]) =>
tf.traverse(fa)(f)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package higherkindness.droste.derivation

import cats.*

/*
Credit to the `kittens` project. This is a direct copy/paste from there
https://github.com/typelevel/kittens/blob/5b3b7ca168b9636f340b23bc95a4fd1506a26707/core/src/main/scala-3/cats/derived/package.scala
*/

extension (F: Foldable.type)
inline def derived[F[_]]: Foldable[F] = DerivedFoldable[F]

extension (F: Functor.type)
inline def derived[F[_]]: Functor[F] = DerivedFunctor[F]

extension (F: Traverse.type)
inline def derived[F[_]]: Traverse[F] = DerivedTraverse[F]

object semiauto:

inline def foldable[F[_]]: Foldable[F] = DerivedFoldable[F]

inline def functor[F[_]]: Functor[F] = DerivedFunctor[F]

inline def traverse[F[_]]: Traverse[F] = DerivedTraverse[F]


object auto:

inline given [F[_]]: Foldable[F] = DerivedFoldable[F]

inline given [F[_]]: Functor[F] = DerivedFunctor[F]

inline given [F[_]]: Traverse[F] = DerivedTraverse[F]
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package higherkindness.droste.derivation

import cats.{Foldable, Functor, Traverse}

class FoldableTests {

case class Box[A](value: A) derives Foldable

sealed trait Maybe[+A] derives Foldable
case object Nufin extends Maybe[Nothing]
case class Just[A](value: A) extends Maybe[A]

sealed trait CList[A] derives Foldable
case object CNil extends CList[Nothing]
case class CCons[A](head: A, tail: CList[A]) extends CList[A]
}


class FunctorTests {

case class Box[A](value: A) derives Functor

sealed trait Maybe[+A] derives Functor
case object Nufin extends Maybe[Nothing]
case class Just[A](value: A) extends Maybe[A]

sealed trait CList[A] derives Functor
case object CNil extends CList[Nothing]
case class CCons[A](head: A, tail: CList[A]) extends CList[A]

}


class TraverseTests {

case class Box[A](value: A) derives Traverse

sealed trait Maybe[+A] derives Traverse
case object Nufin extends Maybe[Nothing]
case class Just[A](value: A) extends Maybe[A]

sealed trait CList[A] derives Traverse
case object CNil extends CList[Nothing]
case class CCons[A](head: A, tail: CList[A]) extends CList[A]
}
2 changes: 1 addition & 1 deletion project/ProjectPlugin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ object ProjectPlugin extends AutoPlugin {
run / connectInput := true,
Global / cancelable := true,
crossScalaVersions := List(ScalaV.v212, ScalaV.v213, ScalaV.v3),
scalaVersion := ScalaV.v213,
scalaVersion := ScalaV.v3,
libraryDependencies ++= on(2)(
compilerPlugin(
"org.typelevel" % "kind-projector" % "0.13.0" cross CrossVersion.full
Expand Down

0 comments on commit 6c377a3

Please sign in to comment.