Skip to content

Instantly share code, notes, and snippets.

@melvic-ybanez
Last active January 12, 2026 14:27
Show Gist options
  • Select an option

  • Save melvic-ybanez/04638dfea49bd39b856562a7e393a573 to your computer and use it in GitHub Desktop.

Select an option

Save melvic-ybanez/04638dfea49bd39b856562a7e393a573 to your computer and use it in GitHub Desktop.

Revisions

  1. melvic-ybanez revised this gist Jan 3, 2021. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions what-i-didnt-know-about-fp-2020.md
    Original file line number Diff line number Diff line change
    @@ -166,4 +166,6 @@
    ```
    Apparently, the issue happens in the implementation of `index`. We can't ensure, at the type-level, that the sum type constructor we'd get would provide us with the type we need. Same thing can be said with the other sum types, such as `List`.

    ---

    Next List: [What I Didn't Know about Functional Programming until 2021](https://gist.github.com/melvic-ybanez/acaa3b7fd6b0ba176975e6f4b10e67e1)
  2. melvic-ybanez revised this gist Jan 3, 2021. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion what-i-didnt-know-about-fp-2020.md
    Original file line number Diff line number Diff line change
    @@ -164,4 +164,6 @@
    }
    }
    ```
    Apparently, the issue happens in the implementation of `index`. We can't ensure, at the type-level, that the sum type constructor we'd get would provide us with the type we need. Same thing can be said with the other sum types, such as `List`.
    Apparently, the issue happens in the implementation of `index`. We can't ensure, at the type-level, that the sum type constructor we'd get would provide us with the type we need. Same thing can be said with the other sum types, such as `List`.

    Next List: [What I Didn't Know about Functional Programming until 2021](https://gist.github.com/melvic-ybanez/acaa3b7fd6b0ba176975e6f4b10e67e1)
  3. melvic-ybanez revised this gist Dec 30, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion what-i-didnt-know-about-fp-2020.md
    Original file line number Diff line number Diff line change
    @@ -164,4 +164,4 @@
    }
    }
    ```
    Apparently, the issue happens in the implementation of `index`. We can't ensure, at the type-level, that the sum type constructor we'd get provides the type we need.
    Apparently, the issue happens in the implementation of `index`. We can't ensure, at the type-level, that the sum type constructor we'd get would provide us with the type we need. Same thing can be said with the other sum types, such as `List`.
  4. melvic-ybanez revised this gist Dec 30, 2020. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions what-i-didnt-know-about-fp-2020.md
    Original file line number Diff line number Diff line change
    @@ -132,7 +132,7 @@
    ```

    We wrote `Repr => A` for the hom-set `Hom(a, x)`, the codomain of `Hom(a, -)`. Tabulate turns a function into a functor/datastructure (like memoizing a function) while `index` takes a functor and a "key" and yields a value.
    1. You can't have a representable functor for a sum type. The reason for this can be explain using algebra of types. That is, for every representable functor `F` that is isomorphic to `Hom(a, -)`, we can equate their types using exponentials:
    1. You can't have a representable functor for a sum type. The reason for this can be explain using the algebra of types. That is, for every representable functor `F` and hom-functor `Hom(a, -)`, we can equate their types using exponentials:
    ```
    Hom(a, -) <=> F
    Hom(a, x) <=> F x
    @@ -145,13 +145,13 @@
    a = log F
    ```

    With the equations above, we can tell that if `F` is a product type, then `a` is a sum type. We've already seen something like this before:
    With the equations above, we can tell that if `F` is a product type, then `a` is a sum type (we've already seen something like this before):
    ```
    let a := c + d
    (-) ^ a <=> ((-) ^ c)((-) ^ d)
    ```

    This means we can have a representable to be a product. However, if `F x` was a sum type, things would be different. This is easier to show using code:
    This means we can have a product for `F x`. However, if `F x` was a sum type, things would be different. This is easier to show using code:
    ```scala
    def repEither: Representable[Either[String, *]] = new Representable[Either[String, *]] {
    type Repr = Int
  5. melvic-ybanez revised this gist Dec 30, 2020. 1 changed file with 34 additions and 1 deletion.
    35 changes: 34 additions & 1 deletion what-i-didnt-know-about-fp-2020.md
    Original file line number Diff line number Diff line change
    @@ -131,4 +131,37 @@
    }
    ```

    We wrote `Repr => A` for the hom-set `Hom(a, x)`, the codomain of `Hom(a, -)`. Tabulate turns a function into a functor/datastructure (like memoizing a function) while `index` takes a functor and a "key" and yields a value.
    We wrote `Repr => A` for the hom-set `Hom(a, x)`, the codomain of `Hom(a, -)`. Tabulate turns a function into a functor/datastructure (like memoizing a function) while `index` takes a functor and a "key" and yields a value.
    1. You can't have a representable functor for a sum type. The reason for this can be explain using algebra of types. That is, for every representable functor `F` that is isomorphic to `Hom(a, -)`, we can equate their types using exponentials:
    ```
    Hom(a, -) <=> F
    Hom(a, x) <=> F x
    x ^ a = F x // remember exponentials

    // generalizing...
    (-) ^ a = F

    // with simple algebraic manipulation...
    a = log F
    ```

    With the equations above, we can tell that if `F` is a product type, then `a` is a sum type. We've already seen something like this before:
    ```
    let a := c + d
    (-) ^ a <=> ((-) ^ c)((-) ^ d)
    ```

    This means we can have a representable to be a product. However, if `F x` was a sum type, things would be different. This is easier to show using code:
    ```scala
    def repEither: Representable[Either[String, *]] = new Representable[Either[String, *]] {
    type Repr = Int

    def tabulate[A] = f => Right(f(0))

    def index[A] = {
    case Right(value) => _ => value
    case Left(err) => ??? // what to do here????
    }
    }
    ```
    Apparently, the issue happens in the implementation of `index`. We can't ensure, at the type-level, that the sum type constructor we'd get provides the type we need.
  6. melvic-ybanez revised this gist Dec 30, 2020. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions what-i-didnt-know-about-fp-2020.md
    Original file line number Diff line number Diff line change
    @@ -125,10 +125,10 @@
    trait Representable[F[_]] {
    type Repr

    def tabulate[X]: (Repr => X) => F[X]
    def tabulate[A]: (Repr => A) => F[A]

    def index[X]: F[X] => Repr => X
    def index[A]: F[A] => Repr => A
    }
    ```

    Tabulate turns a function into a functor/datastructure (like memoizing a function) while `index` takes a functor and a "key" and yields a value.
    We wrote `Repr => A` for the hom-set `Hom(a, x)`, the codomain of `Hom(a, -)`. Tabulate turns a function into a functor/datastructure (like memoizing a function) while `index` takes a functor and a "key" and yields a value.
  7. melvic-ybanez revised this gist Dec 29, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion what-i-didnt-know-about-fp-2020.md
    Original file line number Diff line number Diff line change
    @@ -118,7 +118,7 @@
    ```
    For instance, given the free monoid `(List[String], ++, Nil)`, the monoid `(Int, + , 0)` would be the codomain of the morphism `_.length`.
    1. For every pair of objects `a` and `x` in a category `C`, there exists a **hom-functor** `Hom(a, -): C -> Set` that maps x to the _hom-set_ `Hom(a, x)`. `Hom(a, -)` is essentially a shorthand for `x -> Hom(a, x)` (which reminds me of Scala's `_` notation in simple lambdas).
    1. A **representable functor** is any functor `F` that is isomorphic to the hom-functor `Hom(a, -)`, for every category `C` and object `a` in the category `C`. Since `F` and `Hom(a, -)` are functors, their isomorphism and its inverse are _natural transformations_ (and also, because these morphisms follow the naturality conditions).
    1. A **representable functor** is any functor `F` that is isomorphic to the hom-functor `Hom(a, -)`, for every category `C` and object `a` in the category `C`. Since `F` and `Hom(a, -)` are functors, the isomorphism between them and its inverse are _natural transformations_ (and also, because these morphisms follow the naturality conditions).

    The typeclass definition will allow support for memoization of some sort and lookups:
    ```scala
  8. melvic-ybanez revised this gist Dec 29, 2020. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion what-i-didnt-know-about-fp-2020.md
    Original file line number Diff line number Diff line change
    @@ -118,7 +118,9 @@
    ```
    For instance, given the free monoid `(List[String], ++, Nil)`, the monoid `(Int, + , 0)` would be the codomain of the morphism `_.length`.
    1. For every pair of objects `a` and `x` in a category `C`, there exists a **hom-functor** `Hom(a, -): C -> Set` that maps x to the _hom-set_ `Hom(a, x)`. `Hom(a, -)` is essentially a shorthand for `x -> Hom(a, x)` (which reminds me of Scala's `_` notation in simple lambdas).
    1. A **representable functor** is any functor `F` that is isomorphic to the hom-functor `Hom(a, -)`, for every category `C` and object `a` in the category `C`. By definition, the isomorphism between `F` and `Hom(a, -)` and its inverse is a pair of _natural transformations_. These morphisms allow support for memoization and lookups. The typeclass would look like this:
    1. A **representable functor** is any functor `F` that is isomorphic to the hom-functor `Hom(a, -)`, for every category `C` and object `a` in the category `C`. Since `F` and `Hom(a, -)` are functors, their isomorphism and its inverse are _natural transformations_ (and also, because these morphisms follow the naturality conditions).

    The typeclass definition will allow support for memoization of some sort and lookups:
    ```scala
    trait Representable[F[_]] {
    type Repr
  9. melvic-ybanez revised this gist Dec 29, 2020. 1 changed file with 14 additions and 1 deletion.
    15 changes: 14 additions & 1 deletion what-i-didnt-know-about-fp-2020.md
    Original file line number Diff line number Diff line change
    @@ -116,4 +116,17 @@
    ```scala
    def hom[A, M](ma: List[A])(f: A => M)(implicit M: Monoid[M]): M
    ```
    For instance, given the free monoid `(List[String], ++, Nil)`, the monoid `(Int, + , 0)` would be the codomain of the morphism `_.length`.
    For instance, given the free monoid `(List[String], ++, Nil)`, the monoid `(Int, + , 0)` would be the codomain of the morphism `_.length`.
    1. For every pair of objects `a` and `x` in a category `C`, there exists a **hom-functor** `Hom(a, -): C -> Set` that maps x to the _hom-set_ `Hom(a, x)`. `Hom(a, -)` is essentially a shorthand for `x -> Hom(a, x)` (which reminds me of Scala's `_` notation in simple lambdas).
    1. A **representable functor** is any functor `F` that is isomorphic to the hom-functor `Hom(a, -)`, for every category `C` and object `a` in the category `C`. By definition, the isomorphism between `F` and `Hom(a, -)` and its inverse is a pair of _natural transformations_. These morphisms allow support for memoization and lookups. The typeclass would look like this:
    ```scala
    trait Representable[F[_]] {
    type Repr

    def tabulate[X]: (Repr => X) => F[X]

    def index[X]: F[X] => Repr => X
    }
    ```

    Tabulate turns a function into a functor/datastructure (like memoizing a function) while `index` takes a functor and a "key" and yields a value.
  10. melvic-ybanez revised this gist Dec 29, 2020. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions what-i-didnt-know-about-fp-2020.md
    Original file line number Diff line number Diff line change
    @@ -45,9 +45,9 @@
    ```
    This means `Either[B, C] => A` can be broken down into a pair of functions `B => A` and `C => A`, though in Scala, it's usually just implemented using pattern matching:
    ```scala
    def handleTransactionResult(result: Either[NotFound, User]) = result match {
    case NotFound => ???
    case user: User => ???
    def handleTransactionResult(result: Either[ErrorCode, User]) = result match {
    case Left(error) => ???
    case Right(user) => ???
    }
    ```
    In Haskell, the syntax of the pattern matching (not the `case of` one) gives more emphasis on the idea of separating the implemenations into smaller functions.
  11. melvic-ybanez revised this gist Dec 28, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion what-i-didnt-know-about-fp-2020.md
    Original file line number Diff line number Diff line change
    @@ -16,7 +16,7 @@

    The cardinality of a sum type is equal to the sum of the cardinalities of its constructors. For instance, `Either[Boolean, Int]` is a set that contains `true`, `false`, and all the possible integers, so it has the cardinality of boolean in addition to the cardinality of the set of all integers.

    The cardinatlity of the function type is equal to the cardinality of the codomain (or logical consequent) raised to the power of the cardinality of the domain (or logical anticendent). In other words, `A => B` has the cardinality of `|B| ^ |A|`.
    The cardinatlity of the function type is equal to the cardinality of the codomain (or logical consequent) raised to the power of the cardinality of the domain (or logical antecedent). In other words, `A => B` has the cardinality of `|B| ^ |A|`.
    1. The category of sets, **Set**, is a _monoidal category_ with respect to both product and coproduct types. `Unit`, being a singleton set, is the empty operator or neutral element for product types while `Nothing` (or `Void` in Haskell), being the empty set, is the empty operator for coproducts.
    1. The basic building blocks of algebraic data types are the constant of unit, `const(())`, and the identity on any type `A`.
    1. The functors I've been using the most are covariants, particulary _covariant endofunctors_.
  12. melvic-ybanez revised this gist Dec 28, 2020. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions what-i-didnt-know-about-fp-2020.md
    Original file line number Diff line number Diff line change
    @@ -27,7 +27,7 @@
    def contramap[A, B](f: B => A): F[A] => F[B]
    ```

    It maps the same objects as the covariant functor, but the morphisms are reversed. This is because we are mapping a morphism to the functor of it's dual.
    It maps the same objects as the covariant functor, but the morphism of the domain category (in the case of `fmap`, the function `A => B`) is reversed. This is because we are mapping a morphism `B => A` to the functor of it's dual.

    To refresh our knowledge of functors, an _endofunctor_ is a functor where the domain and codomain categories are the same. Since Scala always deal with the category of types/sets and functions, all of its functors are endofunctors. So we can drop the endo prefix for brevity.
    1. The application of `const` to the value of type `Unit` is isomorphic to the unit function `unit[A]: A => Unit`. In the study of exponentials and algebraic data types, both represents the identity for the powers of one.
    @@ -60,7 +60,7 @@
    def >=>[M[_], A, B, C](f: A => M[B])(g: B => M[C])(implicit M: Monad[M]): A => M[C] =
    a => M.flatMap(f(a))(g)
    ```
    1. The relationship between monads and Kleisli's `>=>` helps explains why monads are so prevalent in functional programming. Imagine solving a complex problem. The functional way of doing it is by recursively decomposing it into smaller sub-problems, each implemented as a simple function, and then composing them all back to solve the whole problem. In practice, however, things might not be easy to compose directly because some, if not all, of these functions might return effectful computations (i.e. values wrapped in some contexts).
    1. The relationship between monads and Kleisli's `>=>` helps explain why monads are so prevalent in functional programming. Imagine solving a complex problem. The functional way of doing it is by recursively decomposing it into smaller sub-problems, the solution of each implemented as a simple function, and then composing all these functions back to solve the whole problem. In practice, however, things might not be easy to compose directly because some, if not all, of these functions might return effectful computations (i.e. values wrapped in some contexts).

    To glue together these effectful computations (or "embellished types" according to Bartosz Milewski), you would need the fish operator and/or monads (or even monad transformers, if the embellishments aren't the same).

    @@ -108,7 +108,7 @@
    | -----------------------------------| ------------------------------------ |
    | `List[A]` | `Free[F[_], A]` |
    | `Nil` | `Return[F[_], A](a: A)` |
    | `Const[A](head: A, tail: List[A])` | `Bind[F[_], A](fa: F[Free[F[_], A]])`|
    | `Const[A](head: A, tail: List[A])` | `Bind[F[_], A](fa: F[Free[F, A]])` |

    This should also remind you of the `return` (or `unit`) and `join` operators of monad. `Return` contains just `a`, without the functor application, while `Bind` applies the functor `F` to the free monad, making it a recursive data structure, like list.

  13. melvic-ybanez revised this gist Dec 27, 2020. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions what-i-didnt-know-about-fp-2020.md
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,7 @@
    # What I Didn't Know about Functional Programming until 2020

    1. Programming using a series of transformations and aggregations, something I've been doing for years, is known as _programming in the map/reduce style_.
    1. The more abstract the type is, the greater its cardinality, and the smaller the set of operations it supports. So make use of _universal quantifiers_, particularly by implementing **fully parametric functions**. They guide you on how to implement their term-level definitions by narrowing the number of possible implementations. In other words, the type system of Scala (or Haskell, for that matter) is not only great for capturing compile-time errors, but is also capable of leading you to the correct solution.
    1. The more abstract the type is, the greater its cardinality, and the smaller the set of operations it supports. So make use of _universal quantifiers_, particularly by implementing **fully parametric functions**. They guide you on how to implement their term-level definitions by narrowing down the number of possible implementations. In other words, the type system of Scala (or Haskell, for that matter) is not only great for capturing compile-time errors, but is also capable of leading you to the correct solution.
    1. You can encode union types by combining different Scala features such as type constructors, subtyping and implicits, and by taking advantage of the [Curry-Howard Isomorphism](https://bit.ly/3egBA0R) and [De Morgan's Laws](https://en.wikipedia.org/wiki/De_Morgan%27s_laws) for negation. Miles Sabin has [a well-written blog post](https://milessabin.com/blog/2011/06/09/scala-union-types-curry-howard/) on how to implement this in Scala.
    1. According to the _Curry-Howard Isomorphism_ --- the correspondence between logic and computation --- _conjunction_ corresponds to _product types_, _disjunction_ to _sum types_, _negation_ to `A => Nothing` type (for all `A`), and _implication_ to _arrows/morphisms_. Scala encodes _universal quantification_ via parametric polymorphism. So `def identity[A]: A => A`, for instance, can be read as "For all `A`, it is the case that `A` implies `A`". _Existential quantification_, on the other hand, can be encoded using abstract type members. The correspondence is shown below:
    | Scala | Logic |
    @@ -16,7 +16,7 @@

    The cardinality of a sum type is equal to the sum of the cardinalities of its constructors. For instance, `Either[Boolean, Int]` is a set that contains `true`, `false`, and all the possible integers, so it has the cardinality of boolean in addition to the cardinality of the set of all integers.

    The cardinatlity of the function type is equal to the cardinality of the codmain (or logical consequent) raised to the power of the cardinality of the domain (or logical anticendent). In other words, `A => B` has the cardinality of `|B| ^ |A|`.
    The cardinatlity of the function type is equal to the cardinality of the codomain (or logical consequent) raised to the power of the cardinality of the domain (or logical anticendent). In other words, `A => B` has the cardinality of `|B| ^ |A|`.
    1. The category of sets, **Set**, is a _monoidal category_ with respect to both product and coproduct types. `Unit`, being a singleton set, is the empty operator or neutral element for product types while `Nothing` (or `Void` in Haskell), being the empty set, is the empty operator for coproducts.
    1. The basic building blocks of algebraic data types are the constant of unit, `const(())`, and the identity on any type `A`.
    1. The functors I've been using the most are covariants, particulary _covariant endofunctors_.
  14. melvic-ybanez revised this gist Dec 27, 2020. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions what-i-didnt-know-about-fp-2020.md
    Original file line number Diff line number Diff line change
    @@ -110,9 +110,9 @@
    | `Nil` | `Return[F[_], A](a: A)` |
    | `Const[A](head: A, tail: List[A])` | `Bind[F[_], A](fa: F[Free[F[_], A]])`|

    This should also remind you of the `return` and `join` operators of monad. `Return` contains just `a`, without the functor application, while `Bind` applies the functor `F` to the free monad, making it a recursive data structure, like list.
    This should also remind you of the `return` (or `unit`) and `join` operators of monad. `Return` contains just `a`, without the functor application, while `Bind` applies the functor `F` to the free monad, making it a recursive data structure, like list.

    1. For all monoid `M`, there exist a _monoid homomorphism_ (otherwise known as _monoid morphism_) from a free monoid `F[A]` to `M`, if we are given a function `A => M`:
    1. For all monoid `M`, there exists a _monoid homomorphism_ (otherwise known as _monoid morphism_) from a free monoid `F[A]` to `M`, if we are given a function `A => M`:
    ```scala
    def hom[A, M](ma: List[A])(f: A => M)(implicit M: Monoid[M]): M
    ```
  15. melvic-ybanez revised this gist Dec 27, 2020. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions what-i-didnt-know-about-fp-2020.md
    Original file line number Diff line number Diff line change
    @@ -112,8 +112,8 @@

    This should also remind you of the `return` and `join` operators of monad. `Return` contains just `a`, without the functor application, while `Bind` applies the functor `F` to the free monad, making it a recursive data structure, like list.

    1. For all monoid `M`, there exist a _monoid homomorphism_ (otherwise known as _monoid morphism_) from a free monoid to `M`:
    1. For all monoid `M`, there exist a _monoid homomorphism_ (otherwise known as _monoid morphism_) from a free monoid `F[A]` to `M`, if we are given a function `A => M`:
    ```scala
    def hom[A, M](ma: List[A])(f: A => B)(implicit M: Monoid[M]): B
    def hom[A, M](ma: List[A])(f: A => M)(implicit M: Monoid[M]): M
    ```
    For instance, the monoid `(Int, + , 0)` would be the codomain of the morphism `_.length`from the free monoid `(List[String], ++, Nil)`.
    For instance, given the free monoid `(List[String], ++, Nil)`, the monoid `(Int, + , 0)` would be the codomain of the morphism `_.length`.
  16. melvic-ybanez revised this gist Dec 27, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion what-i-didnt-know-about-fp-2020.md
    Original file line number Diff line number Diff line change
    @@ -116,4 +116,4 @@
    ```scala
    def hom[A, M](ma: List[A])(f: A => B)(implicit M: Monoid[M]): B
    ```
    For instance, the monoid `(Int, + , 0)` would be the codomain of the monoid homomorphism `_.length`from the free monoid `(List[String], ++, Nil)`.
    For instance, the monoid `(Int, + , 0)` would be the codomain of the morphism `_.length`from the free monoid `(List[String], ++, Nil)`.
  17. melvic-ybanez revised this gist Dec 27, 2020. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions what-i-didnt-know-about-fp-2020.md
    Original file line number Diff line number Diff line change
    @@ -110,6 +110,8 @@
    | `Nil` | `Return[F[_], A](a: A)` |
    | `Const[A](head: A, tail: List[A])` | `Bind[F[_], A](fa: F[Free[F[_], A]])`|

    This should also remind you of the `return` and `join` operators of monad. `Return` contains just `a`, without the functor application, while `Bind` applies the functor `F` to the free monad, making it a recursive data structure, like list.

    1. For all monoid `M`, there exist a _monoid homomorphism_ (otherwise known as _monoid morphism_) from a free monoid to `M`:
    ```scala
    def hom[A, M](ma: List[A])(f: A => B)(implicit M: Monoid[M]): B
  18. melvic-ybanez revised this gist Dec 27, 2020. 1 changed file with 7 additions and 3 deletions.
    10 changes: 7 additions & 3 deletions what-i-didnt-know-about-fp-2020.md
    Original file line number Diff line number Diff line change
    @@ -99,9 +99,9 @@
    def contramap[A, B](stringify: Stringify[A])(f: B => A) = stringify compose f
    }
    ```
    1. **Free Monads** and their signatures can be derived from simply observing the constructors of the **free monoid**. After all, a monad is just a monoid.
    1. **Free Monads** and their signatures can be derived from simply observing the constructors of the _free monoid_. After all, a monad is just a monoid.

    Loosely speaking, a free monoid is a monoid that is free of _undesired information loss_ (or collapsing of elements). The traditional instance would be the `(List[A], ++, Nil)` type for all `A`. The binary operator `++` would not unnecessarily collapse values from lists, and the empty list is the perfect neutral element that follows the identity element axiom.
    Loosely speaking, a **free monoid** is a monoid that is free of _undesired information loss_ (or collapsing of elements). The traditional instance would be the `(List[A], ++, Nil)` monoid for all `A`. The binary operator `++` would not unnecessarily collapse values from lists, and the empty list is the perfect neutral element that follows the identity element axiom.

    By the same token, a free monad is free of unneeded collapsing of functors. You can therefore model the free monad from the `List[A]` type. The correspondence is shown below:
    | Free Monoid | Free Monad |
    @@ -110,4 +110,8 @@
    | `Nil` | `Return[F[_], A](a: A)` |
    | `Const[A](head: A, tail: List[A])` | `Bind[F[_], A](fa: F[Free[F[_], A]])`|


    1. For all monoid `M`, there exist a _monoid homomorphism_ (otherwise known as _monoid morphism_) from a free monoid to `M`:
    ```scala
    def hom[A, M](ma: List[A])(f: A => B)(implicit M: Monoid[M]): B
    ```
    For instance, the monoid `(Int, + , 0)` would be the codomain of the monoid homomorphism `_.length`from the free monoid `(List[String], ++, Nil)`.
  19. melvic-ybanez revised this gist Dec 27, 2020. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions what-i-didnt-know-about-fp-2020.md
    Original file line number Diff line number Diff line change
    @@ -100,7 +100,9 @@
    }
    ```
    1. **Free Monads** and their signatures can be derived from simply observing the constructors of the **free monoid**. After all, a monad is just a monoid.

    Loosely speaking, a free monoid is a monoid that is free of _undesired information loss_ (or collapsing of elements). The traditional instance would be the `(List[A], ++, Nil)` type for all `A`. The binary operator `++` would not unnecessarily collapse values from lists, and the empty list is the perfect neutral element that follows the identity element axiom.

    By the same token, a free monad is free of unneeded collapsing of functors. You can therefore model the free monad from the `List[A]` type. The correspondence is shown below:
    | Free Monoid | Free Monad |
    | -----------------------------------| ------------------------------------ |
  20. melvic-ybanez revised this gist Dec 27, 2020. 1 changed file with 4 additions and 1 deletion.
    5 changes: 4 additions & 1 deletion what-i-didnt-know-about-fp-2020.md
    Original file line number Diff line number Diff line change
    @@ -99,10 +99,13 @@
    def contramap[A, B](stringify: Stringify[A])(f: B => A) = stringify compose f
    }
    ```
    1. **Free Monads** and their signatures can be derived from simply observing the constructors of the **free monoid**. After all, a monad is just a monoid. Loosely speaking, a free monoid is a monoid that is free of _undesired information loss_ (or collapsing of elements). The traditional instance would be the the `List[A]` type for all `A`. By the same token, a free monad is free of unneeded collapsing of functors. You can therefore model the free monad from the `List[A]` type. The correspondence is shown below:
    1. **Free Monads** and their signatures can be derived from simply observing the constructors of the **free monoid**. After all, a monad is just a monoid.
    Loosely speaking, a free monoid is a monoid that is free of _undesired information loss_ (or collapsing of elements). The traditional instance would be the `(List[A], ++, Nil)` type for all `A`. The binary operator `++` would not unnecessarily collapse values from lists, and the empty list is the perfect neutral element that follows the identity element axiom.
    By the same token, a free monad is free of unneeded collapsing of functors. You can therefore model the free monad from the `List[A]` type. The correspondence is shown below:
    | Free Monoid | Free Monad |
    | -----------------------------------| ------------------------------------ |
    | `List[A]` | `Free[F[_], A]` |
    | `Nil` | `Return[F[_], A](a: A)` |
    | `Const[A](head: A, tail: List[A])` | `Bind[F[_], A](fa: F[Free[F[_], A]])`|


  21. melvic-ybanez revised this gist Dec 27, 2020. 1 changed file with 8 additions and 1 deletion.
    9 changes: 8 additions & 1 deletion what-i-didnt-know-about-fp-2020.md
    Original file line number Diff line number Diff line change
    @@ -98,4 +98,11 @@
    implicit val stringifyContravariant: Contravariant[Stringify] = new Contravariant[Stringify] {
    def contramap[A, B](stringify: Stringify[A])(f: B => A) = stringify compose f
    }
    ```
    ```
    1. **Free Monads** and their signatures can be derived from simply observing the constructors of the **free monoid**. After all, a monad is just a monoid. Loosely speaking, a free monoid is a monoid that is free of _undesired information loss_ (or collapsing of elements). The traditional instance would be the the `List[A]` type for all `A`. By the same token, a free monad is free of unneeded collapsing of functors. You can therefore model the free monad from the `List[A]` type. The correspondence is shown below:
    | Free Monoid | Free Monad |
    | -----------------------------------| ------------------------------------ |
    | `List[A]` | `Free[F[_], A]` |
    | `Nil` | `Return[F[_], A](a: A)` |
    | `Const[A](head: A, tail: List[A])` | `Bind[F[_], A](fa: F[Free[F[_], A]])`|

  22. melvic-ybanez revised this gist Dec 23, 2020. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions what-i-didnt-know-about-fp-2020.md
    Original file line number Diff line number Diff line change
    @@ -9,8 +9,8 @@
    | `(Int, String)` | Conjunction |
    | `case class Person(name: String, age: Int)` | Conjunction (since it's just a labelled tuple) |
    | `Either[Int, String]` | Disjunction |
    | `P => Q` | P implies Q. i.e. If P, then Q |
    | `P => Nothing` | Logical negation. i.e. Not P |
    | `P => Q` | P implies Q (i.e., If P, then Q) |
    | `P => Nothing` | Logical negation (i.e., Not P) |
    1. The cardinality of a product type is equal to the product of the cardinalities of each of its constructors, hence the name. For instance, a pair of boolean values has the cardinality of four (i.e `(true, true)`, `(true, false)`, `(false, true)`, `(false, false)`).

  23. melvic-ybanez revised this gist Dec 23, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion what-i-didnt-know-about-fp-2020.md
    Original file line number Diff line number Diff line change
    @@ -6,7 +6,7 @@
    1. According to the _Curry-Howard Isomorphism_ --- the correspondence between logic and computation --- _conjunction_ corresponds to _product types_, _disjunction_ to _sum types_, _negation_ to `A => Nothing` type (for all `A`), and _implication_ to _arrows/morphisms_. Scala encodes _universal quantification_ via parametric polymorphism. So `def identity[A]: A => A`, for instance, can be read as "For all `A`, it is the case that `A` implies `A`". _Existential quantification_, on the other hand, can be encoded using abstract type members. The correspondence is shown below:
    | Scala | Logic |
    | ------------------------------------------- | ---------------------------------------------- |
    | `(Int, String)` | Conjunction |
    | `(Int, String)` | Conjunction |
    | `case class Person(name: String, age: Int)` | Conjunction (since it's just a labelled tuple) |
    | `Either[Int, String]` | Disjunction |
    | `P => Q` | P implies Q. i.e. If P, then Q |
  24. melvic-ybanez revised this gist Dec 23, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion what-i-didnt-know-about-fp-2020.md
    Original file line number Diff line number Diff line change
    @@ -6,7 +6,7 @@
    1. According to the _Curry-Howard Isomorphism_ --- the correspondence between logic and computation --- _conjunction_ corresponds to _product types_, _disjunction_ to _sum types_, _negation_ to `A => Nothing` type (for all `A`), and _implication_ to _arrows/morphisms_. Scala encodes _universal quantification_ via parametric polymorphism. So `def identity[A]: A => A`, for instance, can be read as "For all `A`, it is the case that `A` implies `A`". _Existential quantification_, on the other hand, can be encoded using abstract type members. The correspondence is shown below:
    | Scala | Logic |
    | ------------------------------------------- | ---------------------------------------------- |
    | `Int, String` | Conjunction |
    | `(Int, String)` | Conjunction |
    | `case class Person(name: String, age: Int)` | Conjunction (since it's just a labelled tuple) |
    | `Either[Int, String]` | Disjunction |
    | `P => Q` | P implies Q. i.e. If P, then Q |
  25. melvic-ybanez revised this gist Dec 23, 2020. 1 changed file with 7 additions and 14 deletions.
    21 changes: 7 additions & 14 deletions what-i-didnt-know-about-fp-2020.md
    Original file line number Diff line number Diff line change
    @@ -4,20 +4,13 @@
    1. The more abstract the type is, the greater its cardinality, and the smaller the set of operations it supports. So make use of _universal quantifiers_, particularly by implementing **fully parametric functions**. They guide you on how to implement their term-level definitions by narrowing the number of possible implementations. In other words, the type system of Scala (or Haskell, for that matter) is not only great for capturing compile-time errors, but is also capable of leading you to the correct solution.
    1. You can encode union types by combining different Scala features such as type constructors, subtyping and implicits, and by taking advantage of the [Curry-Howard Isomorphism](https://bit.ly/3egBA0R) and [De Morgan's Laws](https://en.wikipedia.org/wiki/De_Morgan%27s_laws) for negation. Miles Sabin has [a well-written blog post](https://milessabin.com/blog/2011/06/09/scala-union-types-curry-howard/) on how to implement this in Scala.
    1. According to the _Curry-Howard Isomorphism_ --- the correspondence between logic and computation --- _conjunction_ corresponds to _product types_, _disjunction_ to _sum types_, _negation_ to `A => Nothing` type (for all `A`), and _implication_ to _arrows/morphisms_. Scala encodes _universal quantification_ via parametric polymorphism. So `def identity[A]: A => A`, for instance, can be read as "For all `A`, it is the case that `A` implies `A`". _Existential quantification_, on the other hand, can be encoded using abstract type members. The correspondence is shown below:

    ```scala
    (Int, String) // Logical conjunction. i.e. Int and String
    case class Person(name: String, age: Int) // Logical conjunction (since it's just a labelled tuple)
    Either[Int, String] // Logical disjunction. i.e. Int or String
    P => Q // P implies Q. i.e. If P, then Q
    P => Nothing // Logical negation. Not P

    // Universal quantification. "You can swap any pair A and B"
    def swap[A, B](pair: (A, B)): (B, A) = (pair._2, pair._1)
    ```
    | Scala | Logic |
    | ----------------------------------------- | ---------------------------------------------- |
    | case class Person(name: String, age: Int) | Conjunction (since it's just a labelled tuple) |
    | Scala | Logic |
    | ------------------------------------------- | ---------------------------------------------- |
    | `Int, String` | Conjunction |
    | `case class Person(name: String, age: Int)` | Conjunction (since it's just a labelled tuple) |
    | `Either[Int, String]` | Disjunction |
    | `P => Q` | P implies Q. i.e. If P, then Q |
    | `P => Nothing` | Logical negation. i.e. Not P |
    1. The cardinality of a product type is equal to the product of the cardinalities of each of its constructors, hence the name. For instance, a pair of boolean values has the cardinality of four (i.e `(true, true)`, `(true, false)`, `(false, true)`, `(false, false)`).

  26. melvic-ybanez revised this gist Dec 23, 2020. 1 changed file with 4 additions and 0 deletions.
    4 changes: 4 additions & 0 deletions what-i-didnt-know-about-fp-2020.md
    Original file line number Diff line number Diff line change
    @@ -15,6 +15,10 @@
    // Universal quantification. "You can swap any pair A and B"
    def swap[A, B](pair: (A, B)): (B, A) = (pair._2, pair._1)
    ```
    | Scala | Logic |
    | ----------------------------------------- | ---------------------------------------------- |
    | case class Person(name: String, age: Int) | Conjunction (since it's just a labelled tuple) |

    1. The cardinality of a product type is equal to the product of the cardinalities of each of its constructors, hence the name. For instance, a pair of boolean values has the cardinality of four (i.e `(true, true)`, `(true, false)`, `(false, true)`, `(false, false)`).

    The cardinality of a sum type is equal to the sum of the cardinalities of its constructors. For instance, `Either[Boolean, Int]` is a set that contains `true`, `false`, and all the possible integers, so it has the cardinality of boolean in addition to the cardinality of the set of all integers.
  27. melvic-ybanez revised this gist Dec 22, 2020. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions what-i-didnt-know-about-fp-2020.md
    Original file line number Diff line number Diff line change
    @@ -70,14 +70,14 @@
    1. The bind operator `>>=` (or `flatMap` in Scala) is not strictly required to define a monad. All you need are two basic operators: `join` and `return` (or `unit`). Remember that a monad is an object in the category of endofunctors, and so by definition it is an endofunctor and therefore inherits `fmap` (or `map` in Scala). You can define `>>=` in terms of its own `fmap`, and `join`. Here's the minimum (alternative) definition of a monad:
    ```scala
    trait Monad[F[_]] extends Functor[F] {
    def >>=[A, B](monad: F[A])(f: A => F[B]): F[B] = join(map(monad)(f))
    def >>=[A, B](monad: F[A])(f: A => F[B]): F[B] = join(fmap(monad)(f))

    def join[A](monad: F[F[A]]): F[A]

    def unit[A](value: A): F[A]
    }
    ```
    The two basic operators `join` and `unit` corresponds to monoid's binary operation and identity element respectively, an observation that gave rise the infamous quote: "A monad is a monoid in the category of endofunctors".
    The two basic operators `join` and `unit` corresponds to monoid's binary operation and identity element respectively, an observation that gave birth to (or at least demystify) the infamous quote: "A monad is a monoid in the category of endofunctors".
    1. A **Profunctor** is just a _bifunctor_ that is contravariant in its first argument. The _mapping of morphisms_ `dimap`, therefore, is the same as bifunctor's `bimap`, except the domain and codomain of the first argument are switched:
    ```scala
    trait Profunctor[F[_, _]] {
  28. melvic-ybanez revised this gist Dec 22, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion what-i-didnt-know-about-fp-2020.md
    Original file line number Diff line number Diff line change
    @@ -67,7 +67,7 @@

    To glue together these effectful computations (or "embellished types" according to Bartosz Milewski), you would need the fish operator and/or monads (or even monad transformers, if the embellishments aren't the same).

    1. The bind operator `>>=` (or `flatMap` in Scala) is not strictly required to define a monad. All you need are two basic operators: `join` and `return` (or `unit`). Since a monad is an object in the category of endofunctors, then by definition it is an endofunctor and therefore inherits `fmap` (or `map` in Scala). You can define `>>=` in terms of `fmap`, and `join`. Here's the minimum (alternative) definition of a monad:
    1. The bind operator `>>=` (or `flatMap` in Scala) is not strictly required to define a monad. All you need are two basic operators: `join` and `return` (or `unit`). Remember that a monad is an object in the category of endofunctors, and so by definition it is an endofunctor and therefore inherits `fmap` (or `map` in Scala). You can define `>>=` in terms of its own `fmap`, and `join`. Here's the minimum (alternative) definition of a monad:
    ```scala
    trait Monad[F[_]] extends Functor[F] {
    def >>=[A, B](monad: F[A])(f: A => F[B]): F[B] = join(map(monad)(f))
  29. melvic-ybanez revised this gist Dec 22, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion what-i-didnt-know-about-fp-2020.md
    Original file line number Diff line number Diff line change
    @@ -67,7 +67,7 @@

    To glue together these effectful computations (or "embellished types" according to Bartosz Milewski), you would need the fish operator and/or monads (or even monad transformers, if the embellishments aren't the same).

    1. The bind operator `>>=` (or `flatMap` in Scala) is not strictly required to define a monad. All you need are two basic operators, `join` and `return` (or `unit`). Since a monad is an object in the category of endofunctors, then by definition it is an endofunctor and therefore inherits `fmap` (or `map` in Scala). You can define `>>=` in terms of `fmap`, and `join`. Here's the minimum (alternative) definition of a monad:
    1. The bind operator `>>=` (or `flatMap` in Scala) is not strictly required to define a monad. All you need are two basic operators: `join` and `return` (or `unit`). Since a monad is an object in the category of endofunctors, then by definition it is an endofunctor and therefore inherits `fmap` (or `map` in Scala). You can define `>>=` in terms of `fmap`, and `join`. Here's the minimum (alternative) definition of a monad:
    ```scala
    trait Monad[F[_]] extends Functor[F] {
    def >>=[A, B](monad: F[A])(f: A => F[B]): F[B] = join(map(monad)(f))
  30. melvic-ybanez revised this gist Dec 22, 2020. 1 changed file with 1 addition and 2 deletions.
    3 changes: 1 addition & 2 deletions what-i-didnt-know-about-fp-2020.md
    Original file line number Diff line number Diff line change
    @@ -70,8 +70,7 @@
    1. The bind operator `>>=` (or `flatMap` in Scala) is not strictly required to define a monad. All you need are two basic operators, `join` and `return` (or `unit`). Since a monad is an object in the category of endofunctors, then by definition it is an endofunctor and therefore inherits `fmap` (or `map` in Scala). You can define `>>=` in terms of `fmap`, and `join`. Here's the minimum (alternative) definition of a monad:
    ```scala
    trait Monad[F[_]] extends Functor[F] {
    def >>=[A, B](monad: F[A])(f: A => F[B]): F[B] =
    join(map(monad)(f))
    def >>=[A, B](monad: F[A])(f: A => F[B]): F[B] = join(map(monad)(f))

    def join[A](monad: F[F[A]]): F[A]