-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Representable Functor #2284
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
b99e781
Add Representable Functor and RepresentableStore
eli-jordan 0ff7eb9
code review updates
eli-jordan c2c55ea
Merge branch 'master' into representable-functor
kailuowang 7f1c62f
extend AnyVal for representable syntax
eli-jordan cff12a6
Merge branch 'representable-functor' of github.com:eli-jordan/cats in…
eli-jordan c31445e
updates to RepresentableSyntax
eli-jordan File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we could add something similar for
Eval[A]
potentially.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 , Since this has been blocking 1.2.0 release, I propose we tackle that in a separate PR
I added an issue #2320