This is a study note of the concept of FP based on 5-parts tutorial.
1 Motivation and Types
- Small scopes
- DI for loose coupling and easy testing
- Prviate/safe constructor
- Clean APIs
- Good domain modles
A good type system helps you
- describe the stuff: use
- describe the relationship between stuff: use restrict type in upstream
- describe the context of stuff: effects
case class for prodcut type and allows
sealed trait with
case object and
case class to define sum types. The mixture of sum and prodcut types is
algebraic data type (
ADT). Avoid subtyping concept in FP.
For generics, the more kinds of things something can potentially be, the less we can reason abou twhat it actually is. For example,
def foo(s: String): String is less constrained than
defe foo[A](a: A): A because you know very few about the generic version and the only function you can write is
Referential transparency enables you to change a variable with its expression and vice versa. Code can be refactored and be understood locally.
A FP application can be viewed as three nested parts:
- the outmost is the outside world
- the app boundary that has side-effection functions that should be very strict aobut its inputs/outptus
- the core has pure functions
Pure functions are determinstic, have no side effects and are total. If also means no null, no reflection and no exceptions.
An effect is whatever distinguishes
A. An effect is also called a context, a program in
F, and a computation.
Pros of FP in Scala
- expressive domain model
- 1st class functions
- concise generics
- ADTs and pattern matching
- typeclasses over inheritance (with macro of simplicity)
- can be referentially transparent
- reasonably type safe
- model context
Cons of FP in Scala
- different mental model
- mixed FP/OOP
- doesn’t stop you from writing bad code
- runtime DI doesn’t
- mutable state
- side effects
An effects is an impure thing, a box or a context. The extra things for a pure computation. It has a shape of
F[A]. Programming with effects involves
2.1 Operate on things inside of a context: Functor
mapmethod, don’t care about the structure of
Fbut keep the structure.
def map[A, B](x: F[A](f: A => B): F[B]
def lift[A, B](f: A => B): F[A] => F[B] = fa => map(fa)(f). It is called
liftbecause it works at a higher abstraction level.
Functoris a typeclass
- you can constraint a program to require
- you cannot: unwarp a functor, smoosh two functor together like
Functors compose: If
Ghave functors, then
G[F[_]]is a functor.
2.2 Put thing inside of a context: Monad
A monad has the following methods
- put a thing inside a context
A => M[A]. It is called
- unwrap a thing
F[F[A]] => F[A]. It is called
- sequence effects:
F[A] => (A => F[B]) => F[B]. It is called
>>=. The call depends on the previous value. It is used to chain together success cases and short-circuit if something is wrong. It is a much better way than the
All the monads should have the same effect type. It is used to handle success/faliure or contextal staff like reader, writer etc. In a chain of operations, each operation depends on the one before it.
2.3 Work with things nested inside of a context: Applicative
Applicative is used in a china of effectful computatoins that have no relationship with each other. The operation itself is inside a context. The method name is often called
product. It runs multipel indepedent effects concurrently.
The application operation depends on the effect. For
List, it gets all combinations of values in each list. For
Task, it runs multiple tasks concurrently.
2.4 Inverting Containers: Traverse
The inner class must have a type of
Applicative. If an effect has a
Traverse then it has a
sequence method to deal with nested effects.
2.5 Combine effects via unpacking and packing
Check if there is method like
2.6 Nested Monad
for comprehension or use Monda transformers.
2.7 Abstract Over the Context: Higher Kinded Types
F[_] is a type constructor. You can think the
* as a hole in the type.
- Kinds describe the number of holes in a type
- The kind of an ordinary type like
*, zero hole.
- The kind of unary type constructor like
* -> *, one hole.
- The kind of binary type consructor like
* -> * -> *, two holes.
Typeclasses, better to be named type capabilities or type behaviors, are used to
- extend libs with new functionality
- allow ad-hoc polymorphism, no hierarchy/inheritance
- typeclasses can have an OO hierarchy
A type class is decoupled from the data type and it
- is a trait, holds no state
- has a type parameter, can be a higher-kinded type parameter
- has at least one abstract method
- may contain generalized methods
- may extend other typeclasses
There should be one implementation of a typeclass for a given type in a scope, this is called
typeclass coherence. The typeclass defines a
has-a relationship for the type that implement the trait.
Typeclass suppose composition that if
A is a Monoid, you can define
F[A] in a generic way. Typeclass laws are properties that must hold for a typeclass to be valid for the type that implement it. It helps compiler to work properly.
You can use typeclasses to both describe the relationships between stuff and restrain the context.
Applicative are type classes on an effect that restrict the capabilities of an effect.
The differences between a typelcass and an abstract interfaces are
- A type-class instance is decoupled from the data type.
- Typeclasses have laws.
- Typeclasses are composable by compiler.
4 Practical Effect Manipulation
Traverse to invert two effects:
Traversable allows you to transform elements inside the structure like a functor, producing applicative effects along the way, and lift those potentially multiple instances of applicative structure outside of the traversable structure. It is commonly described as a way to traverse a data structure, mapping a function inside a structure while accumulating the applicative contexts along the way.
5 Basics of Tagless Final and ZIO
- of type
F[A], representing whaever differentiates
- stackable, such as
ReaderT[IO]and the entire stack has a type of
F[A], an enviornment comprise one or more effects.
Procedure effects are side-effecting, non-deterministic or partial interaction with the real world. A funcitonal effect is an immutable data structure that describes procedure effects. Functional effects are later interpretted in some runtime into the procedure effects they describe. A functional effect
- a context we operates in that restrict ways you can operate using DSL
- a description of procedure effects
- has referential transparency
- executed by one of many possible interpreters
There are tradeoffs in the DSL and interpreters.
The following code is used to bake a bread:
F[_] denotes the idea of an effect over something. The trait defeines a generic interface of the abrstract “device” or “sevice and is called an
algebra in FP.
The model is defined as the following:
The error is defined as algebraic data type (ADT):
The actual ovens are instances of the
Oven type, are called interpreters. the algebra doesn’t assume anything about that other than defin a set of actions on an interpreter instance. A version could be:
F[_]: Sync chooses the type of effect, also called
capabilities. It means that the effect type needs to be an instance of a
Sync typeclass. In cats, it is the capability of suspending the execution of side-effecting code. Here it can
raise error and
point a function.
To run the code, we need to choose a concrete instance of the
sync type class to be the runtime. The followinmg use the
The runtime can be changed to ZIO, as the following:
The tagless final has four steps:
- define algebra
- define domain model
- define interpreter
- choose a ruttime and an effect type