You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This file contains hidden or 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
A function is a mapping from one set, called a *domain*, to another set, called the *codomain*. A function associates every element in the domain with exactly one element in the codomain. In Scala, both domain and codomain are *types*.
```scala
valsquare:Int=>Int= x => x * x
square(2) // 4
```
## Higher-Order Functions
A *higher-order* function is a function that *accepts* or *returns* a function.
```scala
traitList[A] {
deffilter(f: A=>Boolean):List[A]
}
```
*Example*: `List[A].filter` is a higher-order function that accepts the function `A => Boolean` and returns the value `List[A]`.
## Combinators
*Function combinators* are higher-order functions that accept and return functions.
defboth(left: Conf[A], right: Conf[B]):Conf[(A, B)] = c => (left(c), right(c))
```
*Example*: `both` is a combinator that takes two functions and returns a function.
## Polymorphic Functions
A polymorphic function is one that is universally quantified over one or more type parameters. Scala has no support for polymorphic functions, but they can be emulated via polymorphic methods on traits. A trait modeling a polymorphic function usually has a single method called `apply`, so it can be applied with ordinary function application syntax.
```scala
caseobjectidentity {
defapply[A](value: A):A= value
}
identity(3) // 3
identity("3") // "3"
```
*Example*: This emulates a polymorphic function called `id`, which accepts one type parameter `A`, and a value of type `A`, and returns that same value.
# 2. Mastering Types
## Type Theory 101
A *type* is a compile-time description of a *set of values*. `Int` is the set of all integers between -2147483648 and 2147483647. Values *have* types, which is to say, they may be a member of the set of values they represent.
```scala
2:Int
```
*Example*: `2` is a member of the set of all `Int`. Equivalently, `2` has type `Int`.
## Algebraic Data Types
An algebraic data type is a type formed by composing *product* and *sum* types.
### Product Type
Product types are defined by a *Cartesian cross product* on 2 or more types.
```scala
typePoint2D= (Int, Int)
```
*Example*: A two-dimensional point is a product of a number and a number; each value has *both* an x-coordinate *and* a y-coordinate.
#### Case Classes
In Scala, case classes are the idiomatic representation of product types.
```scala
caseclassPerson(name: String, age: Int)
```
*Example*: A person has *both* a string (`name`) and an integer (`age`).
### Sum Types
Sum types are defined by a *disjoint union* on 2 or more types.
```scala
typeRequestResult=Either[Error, HttpResponse]
```
*Example*: An request result is a sum of `Error` and `HttpResponse`; each value is *either* an error *or* an HTTP response.
#### Sealed Traits
In Scala, sealed traits are the idiomatic representation of sum types (pre-Dotty).
```scala
sealedtraitAddressType
caseobjectHomeextendsAddressType
caseobjectBusinessextendsAddressType
```
*Example*: An `AddressType` is *either* a `Home` or a `Business`, but not both.
## Subtyping
Type `A` is a subtype of `B` if $A \subseteq B$. In Scala, $A$ must *extend* $B$. The compiler allows subtypes to be used wherever a supertype is required.
*Example*: A `Rectangle` is a subtype of `Shape`, because every `Rectangle` is a shape (but not every `Shape` is a `Rectangle`).
## Supertyping
Type `A` is a supertype of `B` if $B \subseteq A$. In Scala, $B$ must *extend* $A$. The compiler allows supertypes to be used wherever a subtype is provided.
In the previous example, `Shape` is a supertype of `Rectangle`, and a variable of type `Shape` may be assigned to a value of type `Rectangle`.
## Universals
A universally quantified type defines a *category* (or *kind*) of types that are all parameterized by some arbitrary type. In Scala, type constructors (such as traits) and methods may be universally quantified, although methods do not have a type (they are a *component* of a type).
## Type Constructors
A type constructor is a universally quantified type, which can be used to construct types.
```
sealed trait List[A]
case class Nil[A]() extends List[A]
case class Cons[A](head: A, tail: List[A]) extends List[A]
```
*Example*: `List` is type constructor, which defines a category of `List`-like types, including `List[Boolean]` (the type of lists of booleans). `List` is said to be *universally quantified* over its type variable `A`.
## Higher-Kinded Types
### Type-Level Functions
Type constructors can be thought of as type-level functions, which accept types and return types. This interpretation is useful when reasoning about higher-kinded types.
*Example*: `List` is a type-level function that accepts one type `A` (the type of its elements), and returns another type `List[A]`. If you pass `Boolean` to `List`, you get back `List[Boolean]`, the type of lists of boolean values.
### Kinds
Kinds can be thought of as "the type of types".
*`*` — The kind of types (the set of all types).
*`* => *` — The kind of type-level functions that accept 1 type (the set of all type-level functions that accept 1 type). The type constructor `List` has kind `* => *`, represented as `_[_]` in Scala.
*`[*, *] => *` — The kind of type-level functions that accept 2 types (the set of all type-level functions that accept 2 types). The type constructor `Either` has kind `[*, *] => *`, represented as `_[_, _]` in Scala.
**Note**: Compare with the types of functions: `A => B`, `A => B => C`, `A => B => C => D`.
### Higher-Order Kinds
Just like functions can be "higher-order", type constructors can be higher-order, too. Scala uses underscores to encode higher-order type constructors. The declaration `trait CollectionModule[Collection[_]]` specifies that `CollectionModule`'s type constructor requires a type constructor of kind `* -> *` (such as `List`).
*`(* => *) => *` — The kind of type constructors that accept a type constructor of kind `* => *`. For example, `trait Functor[F[_]] { ... }` and `trait Monad[F[_]] { ... }`.
**Note**: All higher-order kinds that return a type (`*`) are valid kinds.
## Existentials
An existentially quantified type defines a type that depends on some definite but unknowable type. Existential types are useful for hiding type information that is not globally relevant.
```scala
traitListMap[A] {
typeB
vallist:List[B]
valmapf:B=>A
defrun:List[A] = list.map(mapf)
}
```
*Example*: The type `ListMap[A]#B` is some definite type, but there is no way to know what that type is — it could be anything.
### Skolemization
Every existential type can be encoded as a universal type. This process is called *skolemization*.
```scala
caseclassListMap[B, A](list: List[B], mapf: B=>A)
traitListMapInspector[A, Z] {
defapply[B](value: ListMap[B, A]):Z
}
caseclassAnyListMap[A] {
defapply[Z](value: ListMapInspector[A, Z]):Z
}
```
*Example*: Instead of using `ListMap` directly, we use `AnyListMap`, which allows us to inspect a `ListMap` but only if we can handle any type parameter for `B`.
## Type Lambdas
Functions may be *partially applied* with the underscore operator; e.g. `zip(a, _)`. A type lambda is a way to partially apply a higher-kinded type, which yields another type constructor with fewer type parameters.
Type lambdas are to type constructors as lambdas are to functions; the former are type/value expressions, while the latter are declarations.
```scala
({typeλ[α] =Either[String, α]})#λ
```
*Example*: This is the `Either` type, partially applied with a `String` as the first type parameter.
**Note**: In many (but not all) cases, you can use type aliases instead of type lambdas (e.g. `type EitherString[A] = Either[String, A]`).
### Kind Projector
*Kind Projector* is a common compiler plugin for Scala that provides easier syntax to create type lambdas. For example, the type lambda `({type λ[α] = Either[String, α]})#λ` can be represented with the syntax `Either[String, ?]`. Other syntax can create more complex type lambdas.
<https://github.com/non/kind-projector>
# 3. Mastering Type Classes
A *type class* is a bundle of types and operations defined on them. Most type classes have *laws* that implementations are required to satisfy.
```scala
traitShowRead[A] {
defshow(v: A):String
defread(v: String):Either[String, A]
}
objectShowRead {
defapply[A](implicitv: ShowRead[A]):ShowRead[A] = v
}
```
*Example*: The `ShowRead[A]` type class defines a way of "showing" a type `A` by rendering it to a string, and reading it by parsing it from a string (or producing an error message).
***Right Identity**
```scala
read(show(v)) ==Right(v)
```
***Left Partial Identity**
```scala
read(v).map(show(_)).fold(_ =>true, _ == v)
```
## Instances
A *type class instance*, or simply *instance*, is an implementation of a type class for a given set of types. Such instances are usually made *implicit* so the compiler can thread them through functions that require them.
These types are commonly used to describe optionality and partiality.
```scala
sealedtraitMaybe[A]
finalcaseclassJust [A](value: A) extendsMaybe[A]
finalcaseclassEmpty[A]() extendsMaybe[A]
sealedtrait\/[A, B]
finalcaseclass-\/ [A, B](value: A) extends\/[A, B]
finalcaseclass\/-[B, B](value: B) extends\/[A, B]
typeEither[A, B] =A\/B
sealedtraitValidation[A, B]
finalcaseclassFailure[A, B](value: A) extendsValidation[A, B]
finalcaseclassSuccess[A, B](value: B) extendsValidation[A, B]
```
## Semigroup, Monoid
Semigroups allows combining two things of the same type into another thing of the same type. For example, addition forms a semigroup over integers. Monoids add the additional property of having an "zero" element, which you can append to a value without changing the value.
A functor `F[_]` is a type constructor of kind `* -> *`. In the most general case, an `F[A]` represents a *description* of a computation that may halt, run forever, or produce 0 or more `A`'s.
```scala
traitFunctor[F[_]] {
defmap[A, B](fa: F[A])(ab: A=>B):F[B]
}
```
**Note**: Technically, this is a *covariant endofunctor*, and there are many other types of functors, including invariant and contravariant.
***Identity**
```scala
map(fa)(identity) == fa
```
***Composition**
```scala
map(map(fa)(ab))(bc) == map(fa)(ab.andThen(bc))
```
*Example*: `List` is a functor, and `List[Int]` is a trivial description of a computation producing some number of `Int`'s.
### Natural Transformations
A polymorphic function that maps from one functor `F[_]` to another functor `G[_]` is called a *natural transformation*, and is typically denoted using `F ~> G`. These functions are extremely important in higher-order functional programming.
Often, for some value `X`, `F[X]` is referred to as the "lifted" version of `X`, because it is the same value, but placed "inside" of some functor `F`. For example, you can lift the function `x => x * x` inside `List` by writing `List(x => x * x)`.
### Apply
Some functors implement `Apply`, which provides a way of applying a lifted function `F[A => B]` to some lifted value `F[A]` to yield `F[B]`.
```scala
traitApply[F[_]] extendsFunctor[F] {
defap[A, B](fa: F[A])(fab: F[A=>B]):F[B]
}
```
***Associative Composition**
```scala
ap(ap(fa)(fab))(fbc) ==
ap(fa)(ap(fab)(map(fbc)(_.compose(_).curry))
```
###Applicative
Some functors that implement `Apply` also implement `Applicative`, which provides the ability to lift any value into the functor.
```scala
traitApplicative[F[_]] extendsApply[F] {
defpoint[A](a: A):F[A]
}
```
***Identity**
```scala
ap(fa)(point(_)) == fa
```
***Homomorphism**
```scala
ap(point(a))(point(ab)) == point(ab(a))
```
***Interchange**
```scala
ap(point(a))(fab) == ap(fab)(point(_.apply(a)))
```
***Derived Map**
```scala
map(fa)(ab) == ap(fa)(point(ab))
```
### Bind
Some functors that implement `Apply` also implement `Bind`, which adds the ability to extend a computation `F[A]` with a second computation that depends on the result of `A` (`A => F[B]`), and collapse the result into a single computation `F[B]`.
Containers `F[_]` that define `Traverse` can be traversed in a way that rebuilds the original structure. The typical usage is *sequencing*, which is a way of type-flipping while preserving structure, transforming `F[G[A]]` into `G[F[A]]` for some `F[_]` which has a `Traverse`.
**Notes**: Laws include identity, sequential fusion, purity, naturality, and parallel fusion.
# 5. Mastering Data
## Data & Codata
*Data* is a finite store of information, and may be non-recursive or recursive. Recursive data is sometimes called *inductive data*.
Codata is a description of a process for producing information, and may be non-corecursive or corecursive. Corecursive data is sometimes called *coinductive data*.
Finite data structures can be modeled with data or codata. Infinite data structures are modeled with codata.
## Church Encoding
The lambda calculus, the basis of functional programming, is sufficient to represent all possible data structures in an encoding known as *Church encoding*. Church encoding can sometimes be used to speed up operations, and other times to give you a better understanding of the fundamental properties of some data structure. Church encoding must be faked in Scala because Scala does not have true polymorphic functions (only polymorphic methods).
valzero=newNatural { deffold[Z](zero: =>Z, succ: Z=>Z):Z= zero }
defof(v: Int):Natural=if (v ==0) zero else of(v -1).succ
}
```
## Optics
Optics provide highly-composable, purely-functional machinery for focusing in on a part of a substructure `A` inside a superstructure `S` and performing local transformations to yield a new substructure `B` inside a new superstructure `T`. Different types of optics compose (for example, two lenses can be composed to yield another lens, but a lens and a prism yield an optional).
### Lens
Lenses provide a way to focus on a single term inside a product.
```scala
abstractclassPLens[S, T, A, B] extendsSerializable { self =>
defget(s: S):A
defset(b: B):S=>T
defmodifyF[F[_]:Functor](f: A=>F[B])(s: S):F[T]
defmodify(f: A=>B):S=>T
}
typeLens[S, A] =PLens[S, S, A, A]
```
### Prisms
Prisms provide a way to focus on a single term inside a sum.
```scala
abstractclassPPrism[S, T, A, B] extendsSerializable { self =>
defgetOrModify(s: S):T\/A
defreverseGet(b: B):T
defgetOption(s: S):Option[A]
}
typePrism[S, A] =PPrism[S, S, A, A]
```
### Traversal
Traversals provide a way to focus on zero or more elements.
```scala
abstractclassPTraversal[S, T, A, B] extendsSerializable { self =>
*Recursion schemes* are the ways that recursive and corecursive structures may be traversed, for purposes of altering them or inspecting them. For example, you can "fold" over a list to yield a new value. Using fixed-point data types, you can leverage generic recursion schemes that can work on any type of recursive or corecursive data structure.
### Fixed-Point Types
Fixed-point types factor out the recursion from recursive data structures. They operate on functors `F[_]` and extend them with recursion.
***Fix** — Inductive or coinductive recursive structures. The simplest fixed-point type.
```scala
finalcaseclassFix[F[_]](unFix: F[Fix[F]])
```
***Mu** — Inductive (finite) recursive structures. For example, a JSON data structure.
```scala
typeAlgebra[F[_], A] =F[A] =>A
finalcaseclassMu[F[_]](unMu: Algebra[F, ?] ~>Id)
```
***Nu** — Coinductive (possibly infinite) recursive structures. For example, a stream of values.
```scala
sealed abstract class Nu[F[_]] {
type A
val a: A
val unNu: A => F[A]
}
```
***Cofree[F[_], A]** — A tree-like recursive structure whose nodes are annotated by values of type `A`.
```scala
finalcaseclassCofree[F[_], A](head: A, tail: F[Cofree[F, A]])
```
***Free[F[_], A]** — A tree-like recursive structure whose leaves may be values of type `A`.
```scala
sealedtraitFree[F[_], A]
finalcaseclassPure[F[_], A](value: A) extendsFree[F, A]
Recursive and corecursive type classes allow you to abstract over the fixed-point type used to encode recursion or corecursion.
***Recursive** — `Mu` and `Nu` are recursive.
```scala
traitRecursive[T[_[_]]] {
defproject[F[_]:Functor](t: T[F]):F[T[F]]
}
```
***Corecursive** — `Mu` and `Nu` are corecursive.
```scala
traitCorecursive[T[_[_]]] {
defembed[F[_]:Functor](t: F[T[F]]):T[F]
}
```
###Algebras
Algebras are single-step folds from a structure `F[_]`, while coalgebras are single-step unfolds to a structure `F[_]`. Both folds and unfolds may utilize some monadic effect `M[_]`, which may be the `Identity` monad.
An `IO` abstraction converts effectful computations into pure values. This lets you write purely functional programs that are useful. The values have to be eventually executed, but only in the `main` function of the application.
A `State` monad allows retrieving and updating some arbitrary state `S`. Conceptually, the monad is a function from the old state, to both the new state and a value produced by the function.
defpoint[S, A](a: =>A):Reader[S, A] =Reader(_ => a)
defread[S]:Reader[S, S] =Reader(s => s)
}
```
## Combining Effects
In general, monadic effects do not combine. However, using type classes to abstract over effects enables composition of the requirements for an effect.
Monad transformers are abstractions that provide an effect (such as getting and setting state) that is layered on top of another, more powerful effect.
```scala
traitMonadTrans[F[_[_], _]] {
defliftM[G[_] :Monad, A](a: G[A]):F[G, A]
/** The [[scalaz.Monad]] implied by this transformer. */
Functional programs are comprised from smaller pieces. Small effects compose together to form larger effects. Small pieces of state compose together to form larger state. Small functions compose together to form "larger" functions.
Code that does not interact with external systems is easy to decompose. Code that interacts with external systems — so-called *effects* — can be decomposed in a way that depends on the application's overall architecture for effects.
## Onion Architecture
The *onion architecture* for FP involves layering semantic domains of the application. On the inside, the application speaks at the level of the domain model; at the outside, the application speaks the language of external effects.
*Interpreters* translate from an inner layer to an outer layer.
The onion architecture can be implemented with monad classes and transformers, or with free monads and similar structures of reified computation.
### Reified Computation
The purest and most powerful technique in representing effects involves *reifying* the structure and semantics of effectful computation. Standard abstractions such as the free monad and free applicative are useful.
#### Free Monad
A free monad provides a way to record and playback a sequential tree of operations described by some `F[_]`. Free monads provide a powerful way to describe all manner of effects using ordinary data structures that can be introspected, reflected, played back, and altered at runtime.
```scala
sealedtraitFree[F[_], A] { self =>
finaldefmap[B](ab: A=>B):Free[F, B] =Free.flatMap(self, ab andThen (Free.point[F, B](_)))
Pervasive use of type classes allows polymorphism in the implementation for an effect and supports painless large-scale refactoring. This pattern is associated with monad transformers (*MTL*) but it can be applied to free computations as well.
```scala
traitLogging[F[_]] {
deflog(v: String):F[Unit]
}
objectLogging {
defapply[F[_]](implicitf: Logging[F]):Logging[F] = f
A free applicative provides a way to record and playback a parallel tree of operations described by some `F[_]`. It is not as expressive as a free monad, but it can be introspected all the way to the leaves, without runtime interpretation.
Interpreters for reified computations often have the structure `F ~> T[G, ?]`, where `T` describes the computational context (sequential or parallel, for example), `F` describes the source operations, and `G` describes the target operations.
Interpreters compose *horizontally*. You can compose `Interpreter[T, F, G]` and `Interpreter[T, G, H]` into `Interpreter[T, F, H]`. Stated differently, if you can interpret `F` to `G` in some context `T`, and `G` to `H` (in the same context), then you can interpret `F` to `H`.
***Vertical Composition**
Interpreters compose *vertically*. You can compose `Interpreter[T, F, G]` and `Interpreter[T, F, H]` into `Interpreter[T, F, Product[G, H, ?]]`.
***Monoidal Composition**
Interpreters compose monoidally as per the operations supported by `T[_[_], _]`. For example, if `T` supports a notion of failure, then two `T[F, A]` operations can be appended together, such that the first computation is used if it succeeds, otherwise the second. Racing is another example of monoidal composition possible with parallel computations.
#### Computation Contexts
Computation contexts `T[_[_], _]` are higher-order abstractions that reify computation. `Free` is a sequential context for computation, and a higher-order monad. `FreeAp` is a parallel context for computation, and a higher-order applicative.
#### Applications
Reified computation allows for dynamic introspection and transformation of a program's structure, including weaving effects (by composing interpreters), optimizing program structure (for parallel computational contexts), and even purely-functional mocking.
# Notices
Various code snippets and laws derive from Scalaz, Monocle, and Matryoshka. All rights are reserved via their respective copyright holders.