-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add Representable Functor and RepresentableStore * code review updates * extend AnyVal for representable syntax * updates to RepresentableSyntax
- Loading branch information
1 parent
a298890
commit 186dbab
Showing
20 changed files
with
460 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
package cats | ||
|
||
/** | ||
* Representable. | ||
* | ||
* Is a witness to the isomorphism forall A. F[A] <-> Representation => A | ||
* | ||
* Must obey the laws defined in cats.laws.RepresentableLaws | ||
* i.e. | ||
* tabulate andThen index = identity | ||
* index andThen tabulate = identity | ||
* | ||
* Inspired by the Haskell representable package | ||
* http://hackage.haskell.org/package/representable-functors-3.2.0.2/docs/Data-Functor-Representable.html | ||
*/ | ||
trait Representable[F[_]] extends Serializable { | ||
|
||
def F: Functor[F] | ||
|
||
type Representation | ||
|
||
/** | ||
* Create a function that "indexes" into the `F` structure using `Representation` | ||
*/ | ||
def index[A](f: F[A]): Representation => A | ||
|
||
/** | ||
* Reconstructs the `F` structure using the index function | ||
*/ | ||
def tabulate[A](f: Representation => A): F[A] | ||
} | ||
|
||
private trait RepresentableMonad[F[_], R] extends Monad[F] { | ||
|
||
def R: Representable.Aux[F, R] | ||
|
||
override def pure[A](x: A): F[A] = R.tabulate(_ => x) | ||
|
||
override def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] = | ||
R.tabulate(a => R.index(f(R.index(fa)(a)))(a)) | ||
|
||
override def tailRecM[A, B](a: A)(f: A => F[Either[A, B]]): F[B] = { | ||
R.tabulate { r: R => | ||
@annotation.tailrec | ||
def loop(a: A): B = | ||
R.index(f(a))(r) match { | ||
case Right(b) => b | ||
case Left(a) => loop(a) | ||
} | ||
|
||
loop(a) | ||
} | ||
} | ||
} | ||
|
||
private trait RepresentableBimonad[F[_], R] extends RepresentableMonad[F, R] with Bimonad[F] { | ||
|
||
def M: Monoid[R] | ||
|
||
override def coflatMap[A, B](w: F[A])(f: F[A] => B): F[B] = | ||
R.tabulate(m => f(R.tabulate(x => R.index(w)(M.combine(m, x))))) | ||
|
||
override def extract[A](fa: F[A]): A = | ||
R.index(fa)(M.empty) | ||
} | ||
|
||
object Representable { | ||
type Aux[F[_], R] = Representable[F] { type Representation = R } | ||
|
||
/** | ||
* Summon the `Representable` instance for `F` | ||
*/ | ||
def apply[F[_]](implicit ev: Representable[F]): Representable[F] = ev | ||
|
||
/** | ||
* Derives a `Monad` instance for any `Representable` functor | ||
*/ | ||
def monad[F[_]](implicit Rep: Representable[F]): Monad[F] = new RepresentableMonad[F, Rep.Representation] { | ||
override def R: Representable.Aux[F, Rep.Representation] = Rep | ||
} | ||
|
||
/** | ||
* Derives a `Bimonad` instance for any `Representable` functor whos representation | ||
* has a `Monoid` instance. | ||
*/ | ||
def bimonad[F[_], R](implicit Rep: Representable.Aux[F, R], Mon: Monoid[R]): Bimonad[F] = new RepresentableBimonad[F, R] { | ||
override def R: Representable.Aux[F, R] = Rep | ||
override def M: Monoid[R] = Mon | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package cats.data | ||
|
||
import cats.{Comonad, Functor, Representable} | ||
|
||
/** | ||
* A generalisation of the Store comonad, for any `Representable` functor. | ||
* `Store` is the dual of `State` | ||
*/ | ||
final case class RepresentableStore[F[_], S, A](fa: F[A], index: S)(implicit R: Representable.Aux[F, S]) { | ||
/** | ||
* Inspect the value at "index" s | ||
*/ | ||
def peek(s: S): A = R.index(fa)(s) | ||
|
||
/** | ||
* Extract the value at the current index. | ||
*/ | ||
lazy val extract: A = peek(index) | ||
|
||
/** | ||
* Duplicate the store structure | ||
*/ | ||
lazy val coflatten: RepresentableStore[F, S, RepresentableStore[F, S, A]] = | ||
RepresentableStore(R.tabulate(idx => RepresentableStore(fa, idx)), index) | ||
|
||
def map[B](f: A => B): RepresentableStore[F, S, B] = { | ||
RepresentableStore(R.F.map(fa)(f), index) | ||
} | ||
|
||
/** | ||
* Given a functorial computation on the index `S` peek at the value in that functor. | ||
* | ||
* {{{ | ||
* import cats._, implicits._, data.Store | ||
* | ||
* val initial = List("a", "b", "c") | ||
* val store = Store(idx => initial.get(idx).getOrElse(""), 0) | ||
* val adjacent = store.experiment[List] { idx => List(idx - 1, idx, idx + 1) } | ||
* | ||
* require(adjacent == List("", "a", "b")) | ||
* }}} | ||
*/ | ||
def experiment[G[_]](fn: S => G[S])(implicit G: Functor[G]): G[A] = { | ||
G.map(fn(index))(peek) | ||
} | ||
} | ||
|
||
object RepresentableStore { | ||
|
||
implicit def catsDataRepresentableStoreComonad[F[_], S](implicit R: Representable[F]): Comonad[RepresentableStore[F, S, ?]] = | ||
new Comonad[RepresentableStore[F, S, ?]] { | ||
override def extract[B](x: RepresentableStore[F, S, B]): B = | ||
x.extract | ||
|
||
override def coflatMap[A, B](fa: RepresentableStore[F, S, A])(f: RepresentableStore[F, S, A] => B): RepresentableStore[F, S, B] = | ||
fa.coflatten.map(f) | ||
|
||
override def map[A, B](fa: RepresentableStore[F, S, A])(f: A => B): RepresentableStore[F, S, B] = | ||
fa.map(f) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package cats | ||
package syntax | ||
|
||
trait RepresentableSyntax { | ||
implicit final def catsSyntaxTabulate[A, R](f: R => A): TabulateOps[A, R] = | ||
new TabulateOps[A, R](f) | ||
|
||
implicit final def catsSyntaxIndex[F[_], A](fa: F[A]): IndexOps[F, A] = | ||
new IndexOps[F, A](fa) | ||
} | ||
|
||
final class IndexOps[F[_], A](val fa: F[A]) extends AnyVal { | ||
def index[R](implicit R: Representable.Aux[F, R]): R => A = R.index(fa) | ||
} | ||
|
||
final class TabulateOps[A, R](val f: R => A) extends AnyVal { | ||
def tabulate[F[_]](implicit R: Representable.Aux[F, R]): F[A] = R.tabulate(f) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package cats | ||
package laws | ||
|
||
|
||
/** | ||
* Laws that must be obeyed by any `Representable` functor. | ||
*/ | ||
trait RepresentableLaws[F[_], R] { | ||
|
||
implicit val R: Representable.Aux[F, R] | ||
|
||
def indexTabulateIsId[B](fb: F[B]): IsEq[F[B]] = { | ||
R.tabulate(R.index(fb)) <-> fb | ||
} | ||
|
||
def tabulateIndexIsId[B](f: R => B, x: R): IsEq[B] = { | ||
R.index(R.tabulate(f))(x) <-> f(x) | ||
} | ||
} | ||
|
||
object RepresentableLaws { | ||
def apply[F[_], R](implicit ev: Representable.Aux[F, R]): RepresentableLaws[F, R] = | ||
new RepresentableLaws[F, R] { val R: Representable.Aux[F, R] = ev } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.