Type constructor F[_]
that requires single type with function map
- ability to transform its content, without changing the structure.
You can think of Functor using following intuitions:
- containers (Id, List, Vector, Tree, Option) can apply given function to every element in the collection
- computational context -
map
applies function to a value inside this effect, without changing the effect- Id - no effects
- Const - always return the same value (ignore changes)
- Option - may not have value,
- List/Vector - may have multiple values,
- Either/ValidatedNel/Try - may contain value or error(s)
- Reader - require some context to be computed
- Writer - while computing value, generate some description
- State, IO - computing changes a state
- Monix Task/Future/Twitter Future/Scalaz Task - needs time to be computed
- Map - is indexed with other type (see Controversies about Map)
trait Functor[F[_]] {
def map[A,B](a: F[A])(f: A => B): F[B]
}
-
Functor Implementations:
Scala: Scalaz 7, Scalaz 8, Cats, zio-prelude,
Idris, Purescript, Haskell base, Haskell data-category, Racket algebraic, Racket functional, nLab, Java Mojang/DataFixerUpper -
Encoding close to mathematics: vpatryshev/Categories
-
Formalization in proof assistants: statebox/idris-ct, UniMath, HoTT Book, cubicaltt
-
Functor Laws (Cats, Scalaz 7 Haskell):
- identify:
xs.map(id) == xs
- composition:
xs.map(f).map(g) == xs.map(x => g(f(x))
- identify:
flowchart LR
FA1(("F[A]")) --"map(id)"--> FA2(("F[A]"))
FA(("F[A]")) --"map(f andThen g)"--> FC(("F[C]"))
FA(("F[A]")) --"map(f)"--> FB(("F[B]"))--"map(g)"--> FC(("F[C]"))
If Functor satisfies first law, then it also satisfies second: (Haskell) The second Functor law is redundant - David Luposchainsky
if we don't include bottom values
- (Haskell) contrexample using undefined.
- In Category Theory, functor is a mapping of:
- objects (
F[_]
maps types e.g.Int
toList[Int]
) and - morphisms (if
f
is mapping betweenA
andB
then we can think ofmap(f)
as mapping betweenF[A]
andF[B]
) that - preserves composition and identity - this is ensured by the Functor Laws
- objects (
But in general, functor maps two arbitrary categories. Functor, that maps the same category to itself, is called endo functor
.
So strictly speaking, Functor in programming is Endofunctor
in Category theory.
- Derived methods of Functor:
def lift[A, B](f: A => B): F[A] => F[B] // lift regular function to function inside container
def fproduct[A, B](fa: F[A])(f: A => B): F[(A, B)] // zip elements with result after applying f
def as[A, B](fa: F[A], b: B): F[B] // replace every element with b
def void[A](fa: F[A]): F[Unit] // clear preserving structure
def tupleLeft[A, B](fa: F[A], b: B): F[(B, A)]
def tupleRight[A, B](fa: F[A], b: B): F[(A, B)]
def widen[A, B >: A](fa: F[A]): F[B]
-
Functors can compose: Cats ComposedFunctor compose, Scalaz 7
-
An examples for instances for built in types, function1, and custom Tree type. An examples for usage of map, derived methods, compose.
Functor definition does not require any conditions on type constructor F
or any other operations (unlike Applicative, Monad).
Therefore pretty much everything is a Functor. Notable exceptions are:
- function input arguments (they are in
negative position
) or any input like type - see Contravariant We can define Functor only for return type - because type is inpositive position
. - abstractions where type can be both input and output, see Invariant and blog post Rotten Bananas by Edward Kmett
- abstractions that behave like a Functor but not there are some controversies:
-
Controversies:
- Set: Twitter discussion with explanation done by Mark Seemann Set is not a functor. Comments in alleycats. PR in Scalaz explaining why Set is not a Functor but is a Foldable
- Map: Cats Issue #1831
- Try: Comments in alleycats
-
Many abstractions have enough structure, so we can define
map
that obeys the laws. Such asMonad
defined usingpure
andflatMap
. Another notable example isCoyoneda
that wraps any type constructor and allows to usemap
on it. Functor instance is neccessary only when we want to extract the end result. See Free constructions forFree functors
. -
Resources:
- herding cats - Functor - @eed3si9n (blog post)
- FSiS 1, Type Constructors, Functors, and Kind Projector - Michael Pilquist (video)
- Cats docs
- Scalaz example
- (Haskell) The Extended Functor Family - George Wilson (video)
- Functional Patterns in C++, 1. Functors - Bartosz Milewski (video)
- Understanding Data.Functor.Constant constructor and applicative laws (SO)
Apply is a Functor with superpower to apply function already inside container to container of arguments.
trait Apply[F[_]] extends Functor[F] {
def ap[A, B](ff: F[A => B])(fa: F[A]): F[B]
}
-
Apply Implementations: Cats Scalaz 7 Scalaz 8 Java Mojang/DataFixerUpper
-
- functor laws
ap
composition
def apComposition[A, B, C](fa: F[A], fab: F[A => B], fbc: F[B => C]): Boolean = {
// ap F[A => B] ap F[B => C]
// F[A] ==================> F[B] =================> F[C]
val fb: F[B] = ap(fab)(fa)
val left: F[C] = ap(fbc)(fb)
val expand: (B => C) => ((A => B) => (A => C)) =
(bc: B => C) =>
(ab: A => B) =>
bc compose ab
// map( A=>B => B=>C => A=>C )
// F[B => C] ======================================> F[A=>B => A=>C]
val fabac: F[(A => B) => (A => C)] = map(fbc)(expand)
// ap F[A=>B => A=>C]
// F[A => B] ==============================> F[A => C]
val fac: F[A => C] = ap(fabac)(fab)
// ap F[A=>C]
// F[A] =========================> F[C]
val right: F[C] = ap(fac)(fa)
left == right
}
- Derived methods (there are version defined from
xyz2
untilxyz22
)
def apply2[A, B, Z] (fa: F[A], fb: F[B]) (ff: F[(A,B) => Z]): F[Z]
def apply3[A, B, C, Z](fa: F[A], fb: F[B], fc: F[C])(ff: F[(A,B,C) => Z]): F[Z]
// ...
def map2[A , B, Z] (fa: F[A], fb: F[B]) (f: (A, B) => Z): F[Z]
def map3[A, B, C, Z](fa: F[A], fb: F[B], fc: F[C])(f: (A, B, C) => Z): F[Z]
// ...
def tuple2[A, B] (fa: F[A], fb: F[B]): F[(A, B)]
def tuple3[A, B, C](fa: F[A], fb: F[B], fc: F[C]): F[(A, B, C)]
// ...
def product[A,B](fa: F[A], fb: F[B]): F[(A, B)]
def flip[A, B](ff: F[A => B]): F[A] => F[B]
-
Apply was extracted from Applicative definition and usually is defined as a weaker version of Applicative that cannot put value inside effect F. So, instances for Apply are the same as for Applicative.
-
Apply can compose: Cats compose ComposedApply, Scalaz 7)
-
Resources:
- herding cats - Apply - @eed3si9n (blog post)
- Cats docs
- Scalaz example
- John DeGoes explains why
ap
is useful from Haskell perspective, but in Scala better would be to useproduct
(namedzip
) (reddit)
Applicative Functor is a Functor that can:
- apply function already inside container to container of arguments (so it is Apply)
- put value into container (lift into effect)
trait Applicative[F[_]] extends Apply[F] {
def pure[A](value: A): F[A] // point
}
-
Applicative Implementations: Cats Scalaz 7 Scalaz 8 Haskell, Racket algebraic, Racket functional, Idris Java
1 identify: xs.ap(pure(identity)) == xs
apply identify function lifted inside effect does nothing
def apIdentityLaw[A](fa: F[A]): Boolean = {
val l1: F[A => A] = pure(identity[A])
val l2: F[A] = ap(l1)(fa)
// ap F[identity]
// F[A] ==================> F[A]
l2 == fa
}
2 homomorphism: pure(a).ap(pure(f)) == pure(f(a))
lifting value a and applying lifted function f is the same as apply function to this value and then lift result
def homomorphismLaw[A, B](ab: A => B, a: A): Boolean = {
val fab: F[A => B] = pure(ab)
val fa: F[A] = pure(a)
// F[A => B]
// F[A] =============> F[B]
val l: F[B] = ap(fab)(fa)
val r: F[B] = pure(ab(a))
l == r
}
3 interchange: pure(a).ap(ff) == ff.ap(pure(f => f(a)))
where ff: F[A => B]
def interchangeLaw[A, B](fab: F[A => B], a: A): Boolean = {
// ap F[A => B]
// F[A] ==============> F[B]
val l: F[B] = ap(fab)(pure(a))
val fabb: F[(A => B) => B] = pure((f: A => B) => f(a))
// ap F[(A => B) => B]
// F[A => B] ========================> F[B]
val r: F[B] = ap(fabb)(fab)
l == r
}
4 map: fa.map(f) == fa.ap(pure(f))
def mapLikeDerivedLaw[A, B](f: A => B, fa: F[A]): Boolean = {
val l: F[B] = map(fa)(f)
val r: F[B] = ap(pure(f))(fa)
l == r
}
-
Derived methods - see Apply
-
Applicatives can be composed (Cats compose ComposedApplicative, Scalaz 7)
-
Minimal set of methods to implement Applicative (other methods can be derived from them):
- map2, pure
- apply, pure
-
Resources:
- (Haskell) Typeclassopedia - Applicative
- (Haskell) Applicative Functors - Łukasz Marchewka (video)
- FSiS 2 - Applicative type class - Michael Pilquist (video)
- FSiS 3 - Monad type class - Michael Pilquist (video)
- Cats: docs
- (Haskell) Applicative programming with effects - Conor McBride, Ross Paterson (shorter) longer
- (Haskell) The Essence of the Iterator Pattern - Jeremy Gibbons, Bruno C. d. S. Oliveira: (paper)
- The Essence of the Iterator Pattern - Eric Torreborre (blog post)
- herding cats - Applicative: (blog post)
- Static analysis with Applicatives - Gergő Érdi (blog post)
- Lifting - Tony Morris (blog post)
- (Haskell) Abstracting with Applicatives - Gershom Bazerman (blog post)
- (Haskell) Algebras of Applicatives - Gershom Bazerman (blog post)
- Functional Patterns in C++, 2. Currying, Applicative - Bartosz Milewski (video)
"Extend the Applicative type class with a single method that makes it possible to be selective about effects."
"handle is a selective function application: you apply a handler function of type A => B when given a value of type Left(a), but can skip the handler (along with its effects) in the case of Right(b)."
Andrey Mokhov
trait Selective[F[_]] extends Applicative[F] {
def handle[A, B](fab: F[Either[A, B]], ff: F[A => B]): F[B]
def select[A, B, C](fab: F[Either[A, B]], fac: F[A => C], fbc: F[B => C]): F[C]
}
-
Implementations: Haskell, Scala: cb372/cats-selective, Idris, Agda: tuura/selective-theory-agda
-
Resources:
- Selective functor in sbt - @eed3si9n (blog post)
- Staged Selective Parser Combinators - paper, ICFP 2020 talk
- (Haskell) Selective applicative functors - Andrey Mokhov (blog post), (video)
- (Haskell) Selective Applicative Functors Declare Your Effects Statically, Select Which to Execute Dynamically - Andrey Mokhov, Georgy Lukyanov, Simon Marlow, Jeremie Dimino (paper)
- (OCaml) snowleopard/selective-ocaml
- (Coq) tuura/selective-theory-coq
- (Haskell) Haskell Cafe: Applicative functors with branch/choice? (mail archive)
- copumpkin/SelectiveSigma.hs
- Twitter discussion in what category Selective are monoids or skew monoids
We add to Apply ability flatMap
that can join two computations
and use the output from previous computations to decide what computations to run next.
trait Monad[F[_]] extends Apply[F] {
def pure[A](value: A): F[A]
def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B]
}
-
Implementations:
zio-prelude AssociativeFlatten
Cats FlatMap, Scalaz 7 Bind, Scalaz 8 Bind
Monad: Cats, Scalaz 7, Scalaz 8,
Haskell, Idris, Purescript, Racket algebraic, Racket functional -
Monad Laws (Cats, Scalaz 7, Haskell Wiki):
- flatmap associativity:
fa.flatMap(f).flatMap(g) == fa.flatMap(a => f(a).flatMap(b => g(b))
- left identity:
pure(a).flatMap(f) == f(a)
- right identity:
fa.flatMap(a => pure(a)) == fa
- flatmap associativity:
def flatMapAssociativity[A,B,C](fa: M[A], f: A => M[B], g: B => M[C]): Boolean = {
// flatMap(f)
// M[A] =============> M[B]
val l1: M[B] = flatMap(fa)(f)
// flatMap(g)
// M[B] =============> M[C]
val l2: M[C] = flatMap(l1)(g)
val r1: A => M[C] = a => flatMap(f(a))(b => g(b))
// flatMap(f flatMap g)
// M[A] ======================> M[C]
val r2: M[C] = flatMap(fa)(r1)
l2 == r2
}
def leftIdentity[A,B,C](a: A, f: A => M[B], g: B => M[C]): Boolean = {
// pure
// A =======> M[A]
val l1: M[A] = pure(a)
// flatMap
// M[A] ==========> M[B]
val l2: M[B] = flatMap(l1)(f)
// A =======> M[B]
val r: M[B] = f(a)
l2 == r
}
def rightIdentity[A,B,C](a: A, fa: M[A], g: B => M[C]): Boolean = {
// flatMap
// M[A] ==========> M[A]
val l: M[A] = flatMap(fa)(pure)
val r: M[A] = fa
l == r
}
-
Minimal set of methods to implement Monad (others can be derived using them):
- pure, flatMap
- pure, flatten, map
- pure, flatten, apply
- pure, flatten, map2
-
Derived methods:
def flatten[A](ffa: F[F[A]]): F[A]
def sequence[G[_], A](as: G[F[A]])(implicit G: Traverse[G]): F[G[A]]
def traverse[A, G[_], B](value: G[A])(f: A => F[B])(implicit G: Traverse[G]): F[G[B]]
def replicateA[A](n: Int, fa: F[A]): F[List[A]]
def unit: F[Unit] // put under effect ()
def factor[A, B](ma: F[A], mb: F[B]): F[(A, B)]
-
Monads do not compose Tony Morris blog post. You can use Monad Transformer that know what monad is inside (OptionT, EitherT, ListT) or Free Monads or Eff Monad.
-
Monads can't be filtered. You can use Moand Filter for that.
-
Example (translated from John Huges mind blowing workshop: Monads and all that) instance for custom Tree and usage of flatMap to implement functions zip and number (using State Int).
-
Resources
- FSiS 3 - Monad type class - Michael Pilquist (vido 14:44)
- herding cats - Monad blog post
- Cats docs
- (Haskell) Monads for functional programming - Philip Wadler (paper)
- (Haskell) Monads are Trees with Grafting - A Neighborhood of Infinity - Dan Piponi (paper)
- More on Monoids and Monads - (blog post)
- wiki.haskell - Blow your mind - Monad magic
- https://www.quora.com/What-are-some-crazy-things-one-can-do-with-monads-in-Haskell
- (Category Theory) Monads - TheCatsters (video playlist)
- (Type Theory) The Partiality Monad Achim Jung Fest An Intersection of Neighbourhoods - Thorsten Altenkirch (slides) (partial evaluation modeled using Monads)
- Tail Call Elimination in Scala Monads (blog post)
- MonadMask vs MonadBracket - Michael Snoyman blog post
- Functional Patterns in C++, 3. Async API, Monoid, Monad - Bartosz Milewski video
- (Haskell) Trivial Monad solutions - @geophf (blog post 1) (blog post 2)
- Monads in Java - @geophf (blog post)
Wrapper around function from given type. Input type can be seen as some configuration required to produce result.
case class Reader[-In, +R](run: In => R) {
def map[R2](f: R => R2): Reader[In, R2] =
Reader(run andThen f)
def flatMap[R2, In2 <: In](f: R => Reader[In2, R2]): Reader[In2, R2] =
Reader(x => f(run(x)).run(x))
}
-
Reader can be used to implement dependency injection.
-
Monad instance can be defined for Reader.
-
Resources
- The Reader Monad for Dependency Injection - Json Arhart (video)
- FSiS 9 - Reader, ReaderT, Id - Michael Pilquist (video)
- https://gist.github.com/Mortimerp9/5384467
- Resources
Abstraction over stateful computation.
case class State[S,A](runState: S => (A,S))
You can define monad instance for state:
def stateMonad[S]: Monad[State[S, ?]] = new Monad[State[S, ?]] {
def pure[A](a: A): State[S, A] = State(s => (a, s))
def flatMap[A, B](ma: State[S, A])(f: A => State[S, B]): State[S, B] = State[S, B](
s => {
val (a: A, s2: S) = ma.runState(s)
f(a).runState(s2)
}
)
}
You can define useful operations on state. This could be abstract over in StateMonad
typeclass:
trait StateMonad[F[_],S] extends Monad[F] {
def update: (S => S) => F[S]
def set: S => F[S] = s => update( const(s) )
def fetch: F[S] = update(identity)
}
- Resources
- Towards an Effect System in Scala, Part 1: ST Monad (blog post)
- Scalaz State Monad - Michael Pilquist (video)
- Memoisation with State using Scala - Tony Morris (blog post)
- Monads to Machine Code - Stephen Diehl (blog post) explore JIT compilation and LLVM using IO Monad and State Monad
- Immutability, Docker, and Haskell's ST type - Michael Snoyman
You can define useful operations on State. This could be abstract over in StateMonad
typeclass:
trait StateMonad[F[_],S] extends Monad[F] {
def update: (S => S) => F[S]
def set: S => F[S] = s => update( const(s) )
def fetch: F[S] = update(identity)
}
- Resources
- (Haskell) Monadic Parser Combinators - Graham Hutton, Erik Meijer (1996)
- (Haskell) parsec Haskell monadic parser combinator library
- Parser combinators library: izeigerman/parsecat
- Resources
- (Haskell) Life after monads - robinp (blog post)
- (Haskell) Tying the knot, redux - Dan Rosen (blog post) (reddit)
- (Haskell) mtl/Control.Monad.RWS
- Resources
- (Haskell) Update Monads: Variation On State Monads - Chris Penner (blog post)
- Adventures in Three Monads - Edward Z. Yang
- LogicT - backtracking monad transformer with fair operations and pruning
-
Implementations: garyb/purescript-indexed-monad
-
Monad Factory: Type-Indexed Monads, Mark Snyder, Perry Alexander
-
indexed RWS monad iravid/irwst IRWS
-
Implementations: Haskell,purescript-supermonad, Agda
-
Resources
- (Haskell) Supermonads and superapplicatives - Jan Bracker and Henrik Nilsson (pdf
-
Applications:
- Is there a real-world applicability for the continuation monad outside of academic use?
- snoyberg/conduit
- byorgey/MonadRandom Strict, Lazy
- mrkkrp/megaparsec
- gitpan/Perl6-Pugs
- snapframework/heist
- simonmar/monad-par
- mvoidex/hsdev
- paolino/reactivegas Server, Passo (1) (2), Interazione
- motemen/jusk
- aleino/thesis
- orbitz/web_typed
- exFalso/Sim
- chris-taylor/Haskeme
- vpetro/heopl
- Rabbit: A Compiler for Scheme/Chapter 8 D. Conversion to Continuation-Passing Style
-
Resources
- Cats src
- gist by xuwei-k (https://gist.github.com/xuwei-k/19c9bb8c3afd08175762957880c57500)
- Continuation monad in Scala - Tony Morris (blog post)
- (Haskell) School of Haskell - The Mother of all Monads - Dan Piponi (blog post)
- (Haskell) Haskell for all - The Continuation Monad - Gabriella Gonzalez (blog post)
- Resources
- Resources
- https://www.pusher.com/sessions/meetup/london-functional-programmers/interview-pro-tip-travel-through-time
- https://rosettacode.org/wiki/Water_collected_between_towers
- http://landcareweb.com/questions/33409/haskellde-ni-xiang-xing-cong-tardisdao-revstate
- http://hackage.haskell.org/package/tardis/docs/Control-Monad-Tardis.html
- https://kcsongor.github.io/time-travel-in-haskell-for-dummies/
- https://www.reddit.com/r/haskell/comments/199po0/can_the_tardis_monad_be_used_in_an_efficient_way/
- https://repl.it/@Ouroboros2/Haskell-Tardis-1
- http://blog.sigfpe.com/2006/08/you-could-have-invented-monads-and.html
trait Dijkstra[S,A] {
def wp[Omega](f: (S,A) => Omega): S => Omega
}
is a Monad:
implicit def dijakstraMonad[S]: Monad[Dijkstra[S, ?]] = new Monad[Dijkstra[S, ?]] {
def pure[A](a: A): Dijkstra[S,A] = new Dijkstra[S,A] {
def wp[Omega](f: (S, A) => Omega): S => Omega =
s => f(s,a)
}
def flatMap[A,B](ma: Dijkstra[S,A])(f: A => Dijkstra[S,B]): Dijkstra[S,B] =
new Dijkstra[S,B] {
def wp[Omega](g: (S, B) => Omega): S => Omega =
ma.wp{ case (s,a) => f(a).wp(g)(s) }
}
}
can be created from State:
def fromState[S,A](h: State[S,A]): Dijkstra[S,A] =
new Dijkstra[S,A] {
def wp[Omega](f: (S, A) => Omega): S => Omega =
s => {
val (a,s2) = h.runState(s)
f(s2,a)
}
}
and converted to State:
def fromDijkstra[S,A](k: Dijkstra[S,A]): State[S,A] = State(
s => k.wp{ case(s2,a) => (a,s2) }(s)
)
- Resources
- (Haskell) Dijkstra monads - James Cheney (blgo post)
- Dijkstra Monads in Monadic Computation - Bart Jacobs (paper)
- Dijkstra Monads for Free - Danel Ahman et. al. (paper)
- Verifying Higher order Programs with the Dijkstra Monad - Nikhil Swamy, Juan Chen, Ben Livshits (paper)
- Resources
- (Coq) The Hoare State Monad - Wouter Swierstra (paper)
- Resources
- The Making of an IO - Daniel Spiewak (video)
- Towards an Effect System in Scala, Part 2: IO Monad - (https://apocalisp.wordpress.com/2011/12/19/towards-an-effect-system-in-scala-part-2-io-monad/)
- An IO monad for cats - Daniel Spiewak (blog post)
- typelevel/cats-effect
- Tutorial Monix Task 2.x
- There Can Be Only One...IO Monad - John A De Goes (blog post)
IO that abstract over input, output and potential error. Below implementation based on simplified ZIO from John A De Goes tweet:
case class BIO[R, E, A](run: R => Either[E, A]) {
def map[B](f: A => B): BIO[R, E, B] =
BIO(r => run(r).map(f))
def flatMap[R1 <: R, E1 >: E, B](f: A => BIO[R1, E1, B]): BIO[R1, E1, B] =
BIO(r => run(r).flatMap(a => f(a).run(r)))
}
Classic IO is a Profunctor, for Bifunctor IO one can define also instance of Bifunctor
def zioFunctor[R,E]: Functor[BIO[R,E,*]] = new Functor[BIO[R,E,*]] {
def map[A, B](fa: BIO[R, E, A])(f: A => B): BIO[R, E, B] = fa.map(f)
}
trait BIOMonad[R,E] extends Monad[BIO[R,E,*]] {
def pure[A](a: A): BIO[R, E, A] = BIO(_ => Right(a))
def flatMap[A, B](fa: BIO[R, E, A])(f: A => BIO[R, E, B]): BIO[R, E, B] = fa.flatMap(f)
}
def bioMonad[R,E]: Monad[BIO[R,E,*]] = new BIOMonad[R,E] {}
def bioContravariant[E,A]: Contravariant[BIO[*, E, A]] = new Contravariant[BIO[*,E,A]] {
def contramap[R, RR](fa: BIO[R, E, A])(f: RR => R): BIO[RR, E, A] =
BIO{ rr => fa.run(f(rr)) }
}
def bioProfunctor[E]: Profunctor[BIO[*,E,*]] = new Profunctor[BIO[*, E, *]] {
def dimap[RR, R, A, AA](f: RR => R, g: A => AA): BIO[R, E, A] => BIO[RR, E, AA] = fa =>
BIO{ rr => fa.map(g).run(f(rr)) }
}
def bioBifunctor[R]: Bifunctor[BIO[R, *, *]] = new Bifunctor[BIO[R,*,*]] {
def bimap[E, EE, A, AA](f: E => EE, g: A => AA): BIO[R, E, A] => BIO[R, EE, AA] = fa =>
BIO{ rr =>
fa.run(rr) match {
case Right(a) => Right(g(a))
case Left(b) => Left(f(b))
}
}
}
- Resources
- John A De Goes tweet with simplified ZIO implementation
- Bifunctor IO: A Step Away from Dynamically-Typed Error Handling - John A De Goes (blog post), reddit
- On Bifunctor IO and Java's Checked Exceptions - @alexelcu (blog post), twitter
- LukaJCB/cats-bio, PR to move into cats-effect
- (Idris) base/Control/IOExcept
- Using ZIO with Tagless-Final - John A De Goes (blog post)
- scalaz/scalaz-zio IO docs src
- (Haskell) Combining errors with Bifunctor - Daniel Díaz Carrete (https://medium.com/@danidiaz/combining-errors-with-bifunctor-e7a40970fda9)
- Resources
- The RIO Monad - Michael Snoyman (blog post), snoyberg/rio, reddit
- http4s-tracer motivation
- Resources
Functor that can create covariant functor (by passing identity as g) or contravariant functor (by passing identity to f). It represent situation when given type constructor contains type on both positive and negative position (like function A => A).
trait Invariant[F[_]] {
def imap[A, B](fa: F[A])(f: A => B)(g: B => A): F[B]
}
-
Resources
- Explorations in Variance - Michael Pilquist (video)
- Cats docs
- Exponential on Functor level tweeter Oleg Grenrus
- (Haskell) Rotten Bananas - Edward Kmett (blog post) (There is nice example, beware: a lot of recursion schemas)
- Category Theory 8.1: Function objects, exponentials (video)
Traverse are composable Distributive it require only Functor (and Traverse require Applicative)
trait Distributive[F[_]] extends Functor[F] {
def collect[G[_]:Functor,A,B](fa: G[A])(f: A => F[B]): F[G[B]]
def distribute[G[_]:Functor,A](fa: G[F[A]]): F[G[A]]
}
-
Implementations: Scalaz Cats Haskell distributive/Data.Distributive Purescript purescript-distributive/Data.Distributive
-
Resources
- Scalaz issue about difference between Haskell/Purescript vs Scala impl issue
- The essence of dataflow programming - Tarmo Uustalu, Varmo Vene paper short long slides) (parsing using Monads, Distributive, Comonads)
- (Haskell) Moore for Less - Edward Kmett (blog post)
- (Haskell) Zippers Using Representable And Cofree - Chris Penner (https://chrispenner.ca/posts/representable-cofree-zippers)
- (Haskell) A law for Distributive? (reddit)
- (Haskell) How to write a Representable instance using only Distributive properties? (SO)
- (Haskell) usage of Distributive in old hanshoglund/music-suite
- Is there a concept of something like co-applicative functors sitting between comonads and functors?
Given definition of foldLeft (eager, left to right0) and foldRight (lazi, right to left) provide additional way to fold Monoid.
trait Foldable[F[_]] {
def foldLeft[A, B](fa: F[A], b: B)(f: (B, A) => B): B
def foldRight[A, B](fa: F[A], z: => B)(f: (A, => B) => B): B
}
- Laws: no. You can define condition that foldLeft and foldRight must be consistent.
- Derived methods (are different for scalaz and Cats):
def foldMap[A, B](fa: F[A])(f: A => B)(implicit B: Monoid[B]): B
def foldM [G[_], A, B](fa: F[A], z: B)(f: (B, A) => G[B])(implicit G: Monad[G]): G[B] // foldRightM
def foldLeftM[G[_], A, B](fa: F[A], z: B)(f: (B, A) => G[B])(implicit G: Monad[G]): G[B]
def find[A](fa: F[A])(f: A => Boolean): Option[A] // findLeft findRight
def forall[A](fa: F[A])(p: A => Boolean): Boolean // all
def exists[A](fa: F[A])(p: A => Boolean): Boolean // any
def isEmpty[A](fa: F[A]): Boolean // empty
def get[A](fa: F[A])(idx: Long): Option[A] // index
def size[A](fa: F[A]): Long // length
def toList[A](fa: F[A]): List[A]
def intercalate[A](fa: F[A], a: A)(implicit A: Monoid[A]): A
def existsM[G[_], A](fa: F[A])(p: A => G[Boolean])(implicit G: Monad[G]): G[Boolean] // anyM
def forallM[G[_], A](fa: F[A])(p: A => G[Boolean])(implicit G: Monad[G]): G[Boolean] // allM
// Cats specific
def filter_[A](fa: F[A])(p: A => Boolean): List[A]
def takeWhile_[A](fa: F[A])(p: A => Boolean): List[A]
def dropWhile_[A](fa: F[A])(p: A => Boolean): List[A]
def nonEmpty[A](fa: F[A]): Boolean
def foldMapM[G[_], A, B](fa: F[A])(f: A => G[B])(implicit G: Monad[G], B: Monoid[B]): G[B]
def traverse_[G[_], A, B](fa: F[A])(f: A => G[B])(implicit G: Applicative[G]): G[Unit]
def sequence_[G[_]: Applicative, A](fga: F[G[A]]): G[Unit]
def foldK[G[_], A](fga: F[G[A]])(implicit G: MonoidK[G]): G[A]
// scalaz specific
def filterLength[A](fa: F[A])(f: A => Boolean): Int
def maximum[A: Order](fa: F[A]): Option[A]
def maximumOf[A, B: Order](fa: F[A])(f: A => B): Option[B]
def minimum[A: Order](fa: F[A]): Option[A]
def minimumOf[A, B: Order](fa: F[A])(f: A => B): Option[B]
def splitWith[A](fa: F[A])(p: A => Boolean): List[NonEmptyList[A]]
def splitBy[A, B: Equal](fa: F[A])(f: A => B): IList[(B, NonEmptyList[A])]
def selectSplit[A](fa: F[A])(p: A => Boolean): List[NonEmptyList[A]]
def distinct[A](fa: F[A])(implicit A: Order[A]): IList[A]
-
Can be composed
-
Implementations: Cats Scalaz 7 Scalaz 8 Idris Java DataFixerUpper Java vavr
-
Resources:
- FSiS 4 - Semigroup, Monoid, and Foldable type classes - Michael Pilquist video 55:38
- Cats docs
- Scalaz example
- scalaz Foldable1 src example
- Purescript purescript-foldable-traversable/Data.FoldableWithIndex
- Purescript purescript-foldable-traversable/Data.Semigroup.Foldable
- A tutorial on the universality and expressiveness of fold - Graham Hutton (paper)
- (Haskell) Conquering Folds - Edward Kmett blog post
- (Haskell) Monoidal Sorting - Chris Penner (blog post) (
Monoids
andfoldMap
used for sorting) - Beautiful folds are practical, too - Gabriella Gonzalez (video) slides repo Hacker News
- Beautiful folds in Scala - Adam Warski (blog post)
Functor with method traverse and folding functions from Foldable.
trait Traverse[F[_]] extends Functor[F] with Foldable[F] {
def traverse[G[_]: Applicative, A, B](fa: F[A])(f: A => G[B]): G[F[B]]
}
- Laws: Cats Traverse laws (Haskell) Typeclassopedia
- Derived methods
def sequence[G[_]:Applicative,A](fga: F[G[A]]): G[F[A]]
def zipWithIndex[A](fa: F[A]): F[(A, Int)] // indexed
// ... other helper functions are different for scalaz and cats
-
Implementations: Cats Scalaz 7 Scalaz 8 Idris Purescript Java DataFixerUpper Java vavr-io
-
Resources
- Scalaz example
- Cats docs
- PR for Cats
- scalaz Traverse1
- purescript-foldable-traversable/Data.TraversableWithIndex
- FSiS 5 - Parametricity and the Traverse type class - Michael Pilquist (video)
- The Essence of the Iterator Pattern - Jeremy Gibbons, Bruno C. d. S. Oliveira: (paper)
- An Investigation of the Laws of Traversals - Mauro Jaskelioff, Ondrej Rypacek (paper)
Semigroup that abstracts over type constructor F. For any proper type A can produce Semigroup for F[A].
trait SemigroupK[F[_]] {
def combineK[A](x: F[A], y: F[A]): F[A] // plus
def algebra[A]: Semigroup[F[A]] // semigroup
}
-
SemigroupK can compose
-
Resources:
Monoid that abstract over type constructor F
. For any proper type A
can produce Monoid for F[A]
.
trait MonoidK[F[_]] extends SemigroupK[F] {
def empty[A]: F[A]
override def algebra[A]: Monoid[F[A]] // monoid
}
-
MonoidK can compose
-
Resources:
- Scalaz (src)
- Cats docs src
- FSiS 6 - SemigroupK, MonoidK, MonadFilter, MonadCombine - Michael Pilquist (video 21:15)
-
Implementations Cats
-
Resources
- Finding all permutations of list: (blog post haskell) (translation to Scala using Cats)
- Implementations
- Resources
- Documentation.Layers.Overview (nice overwiev of alternative approaches in Haskell)
"Monad transformers just aren’t practical in Scala." John A De Goes
- Resources
- No More Transformers: High-Performance Effects in Scalaz 8 - John A De Goes (blog post)
- (Haskell) Typeclassopedia Monad transformers
- FSiS 7 - OptionT transformer - Michael Pilquist (video)
- FSiS 8 - EitherT transformer - Michael Pilquist (video)
- (Haskell) The ReaderT Design Pattern - Michael Snoyman (blog post)
- (Haskell) Announcing: monad-unlift - Michael Snoyman (blog post)
- (Haskell) Scanner-parsers II: State Monad Transformers - geophf (blog post)
- Resources
- Eff monad for cats atnos-org/eff github
- b-studios/scala-effekt github
- extensible-effects hackage
- Extensible Effects: an alternative to Monad Transformers - Oleg Kiselyov
- Idris Effect
- bibliography of work related to the theory and practice of computational effects
-
Verified implementations: idris-ct Comma Category, idris-ct Over Category, idris-ct Under Category
-
Resources
- (Theory) Pointwise Kan Extensions - Bartosz Milewski (blog post)
- (Theory) Slice and comma categories 1 - TheCatsters (video)
- (Theory) Slice and comma categories 2 - TheCatsters (video)
- (Theory) Overcategories aka Comma Categories - MathProofsable (video)
- (Theory) Abstract and Concrete Categories The Joy of Cats - Jiri Adamek, Horst Herrlich, George E. Strecker (book)
- (Theory) Functorial Semantics of Algebraic Theories and Some Algebraic Problems in the context of Functorial Semantics of Algebraic Theories - F. William Lawvere - TAC Reprints (book)
- (Theory) Computational Category Theory - D.E. Rydeheard, R.M. Burstall (book)
- (Theory) comma category - nLab
- PR for Cats
- (Haskell) these/Data.Align
Implementations: Cats MTL Haskell
Resources:
- Cats MTL docs
- (Haskell) These, Align, and Crosswalk - Tim Humphries (blog post) (reddit)
Implementations:
- Unfoldable1: Scalaz 8 Purescript
- Unfoldable: Scalaz 8 Purescript Haskell
- Biunfoldable: Haskell
Resources:
"computes the value of a key k, in a side-effect-free way, using a callback of type k → f v to find the values of its dependencies"
type Task c k v = forall f. c f => (k -> f v) -> k -> Maybe (f v)
Resources:
- The Task abstraction - Andrey Mokhov (blog post)
- usage in snowleopard/build
Clojure transducers expressed using types.
type Reducer a r = r -> a -> r
type Transducer a b = forall r . Reducer a r -> Reducer b r
Resources:
- (Haskell) Understanding Clojure transducers through types - Franklin Chen (blog post)
- (Haskell) Transducers are monoid homomorphisms - Oleksandr Manzyuk (blog post)
- (Haskell) Transducers, Mappers and Reducers - giuliohome (blog post)
Monads thats wokrs on different categories.
Resources:
- (Type Theory) Monads Need Not Be Endofunctors - Thorsten Altenkirch, James Chapman, Tarmo Uustalu (paper), (code)
Resources:
For many standard abstractions we can add restriction to be commutative.
Implementations:
Commutative Semigroup ekmett/abelian
Commutative Monoid ekmett/abelian
Commutative Apply Cats src Cats laws
Commutative Applicative Cats src Cats laws ekmett/abelian
Commutative FlatMap Cats src Cats laws
Commutative Monad Cats src Cats laws
Commutative Free Monads ekmett/abelian
Commutative Monad Transformers ekmett/abelian
Commutative Comonads ekmett/abelian
Resources:
- (Haskell) Haskell Live-Coding, Session 1, Commutativity - Edward Kmett (video)
- Are there any interesting commutative monads in Haskell? SO
- The Commutativity monad (blog post) reddit
Resources:
- Functor-Oriented Programming - Russell O’Connor blog post hacker news reddit
- (Haskell) Functor-Of - Vladimir Ciobanu (blog post)