Skip to content

Instantly share code, notes, and snippets.

@stratigos
Last active July 12, 2019 23:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stratigos/5a96f1fadec68291fe1089cf956a79a5 to your computer and use it in GitHub Desktop.
Save stratigos/5a96f1fadec68291fe1089cf956a79a5 to your computer and use it in GitHub Desktop.
Notes on "FSiS Part 1 - Type Constructors, Functors, and Kind Projector"

Functional Structures in Scala

  • ๐Ÿ“† 2019-04-19
  • this spans many weeks / investment times
  • ๐Ÿ“น Screencast: Functional Structures
  • ๐Ÿ‘จโ€๐Ÿ’ป๐Ÿ“๏ธ an experienced OOP programmer learning FP and Scala
    • and maybe category theory? ๐Ÿค”

On Kindedness

  • Cool REPL command: :k TYPE which reveals the Kind of TYPE ๐Ÿ˜Ž
    • e.g.,
      • :k Int => A
      • :k List => F[+A]

Type Constructors

  • A Higher Kinded Type

    • can not be the value of an expression.
    • A List (alone) can not be the value of an expression. But, a List[Thinger] can be.
      • List is the Higher Kinded Type
      • Thinger is the Proper Type
    • A Higher Kinded Type can be thought of as a function that takes a Proper Type and returns a new Proper Type
      • i.e., the F[A], or List[Thinger]
      • This is also called a Type Constructor
  • When defining a function/method where one argument is the result of applying a Proper Type to a Type Constructor, the TC must also be expressed as a type parameter

    • For type parameters: [F[_], A], the F[_] is a Type Constructor
      • specifically, a Type Constructor with one argument
      • the arg could be List(1) or Some(1) or Right(1)
        • but it can not be an Int / A
    • contrived example:
      • def bar[F[_], A](x: F[A], y: F[A]) = null
      • can be called as: bar(Some(1), Some(2))
      • or: bar(List(1,2,3), List(1)
        • ๐Ÿค” ๐Ÿ“ : if this is true, then whatabout F[_] "having only one argument?"
      • it can not be called as: bar(Some(1), List(2)) as that uses two different Proper Types (Option and List), and we only define one type parameter (A)

Functors

  • Functor - a type class that abstracts over Type Constructors that can define a Map operation
    • ๐Ÿ‘€ 7:30
      • code is more clear there ;)
    • Simply put, its abstraction to define a pattern for how to map from one Higher Kinded Type to another
    • expressed as: trait Functor[F[_]] {}
      • will have a method like: def map[A, B](fa: F[A])(f: A => B): F[B]
      • basically, defining map is half of the definintion of a Functor
      • the other half is the set of rules map must comply with

Functor Laws

  • Functor Laws ๐Ÿ‘ฉโ€โš–๏ธ๐Ÿ‘ฎ
    • the set of rules that define how the Functor maps
    • e.g., an identity law that states that for every function fa: F[A], the result of .map(fa)(a => a) must === fa
      • i.e., def identity[F[_], A](fa: F[A]) = Functor[F].map(fa)(a => a) === fa
        • ๐Ÿ“๏ธ === is not real ๐Ÿง™
      • ๐Ÿ‘€ 10:32
        • above code is actually a trait outside of the contrived Functor
        • and since the above code lacks the actual ability to perform said mapping, an implicit can be added to do the work:
        • def identity[...](fa...)(implicit F: Functor[F]) = F.map(fa)(a => a) == fa
    • e.g., composition law states that for F and G, we can compose them together as a single function, or, they can be composed piecewise through many functions
def composition[F[_], A, B, C](fa: F[A], f: A => B, g: B => C)(implicit F: Functor[F]) =
  F.map(F.map(fa)(f))(g) == F.map(fa)(f andThen g)
  • ๐Ÿ‘€๏ธ 14:05 for examples on Functor instances, via a companion object

    • Functor[List] is a valid functor, as our trait was: Functor[F[_]]
      • this implementation is simple, since List already has a map() method
      • as do most std lib collections (Option, etc)
    • Functor[X => ?] -> a function1Functor
    • ๐Ÿ“๏ธ it does not make sense to define a Functor for a Proper Type ๐Ÿ™ƒ๏ธ
    • ๐Ÿ“๏ธ not to be confused with Funktion-One ๐ŸŽถ๏ธ
    • ๐Ÿ‘ทโ€โ™€๏ธ๏ธ [X => ?] is a Type Constructor such that when a Proper Type, X, is applied to it, you get back a function X => A or X => B etc.
    • ๐Ÿ“๏ธ val can not have a type param, only methods (def)
      • and fyi, a type parameter is required to define the X in [X => ?]
    • function1Functor.map() can begin implementation as:
      • def map[A, B](fa: X => A)(f: A => B): X => B = ...
        oh ๐Ÿ’ฉ๏ธ, B has no map ๐Ÿค”๏ธ โ‰
      • we need to return a new function X => B -> the answer is composition
      • def map[A, B](fa: X => A)(f: A => B): X => B = fa andThen f
  • lawfulness is determined by testing the given laws, e.g., identity or
    composition.

    • testing any arbitrary values for the above examples would prove lawfulness
    • ๐Ÿค”๏ธ so how does one define a chaotic functor? inquiring minds want to know!
      • ๐Ÿ˜…๏ธ author describes this at ๐Ÿ‘€๏ธ 23:55
      • def lift[A, B](f: A => B): F[A] => F[B]
        • it is no longer pure as values are wrapped into that context
      • implemented as: def lift[A, B](f: A => B): F[A] => F[B] = fa => map(fa)(f)
        • ๐Ÿค”๏ธ so where did fa come from?
      • def as[A, B](fa: F[A], b: => B): F[B] = map(fa)(_ => b)
      • def void[A](fa: F[A]): F[Unit] = as(fa, ())
  • Break for refreshment @ 27:52


  • ๐Ÿ“† 2019-07-12
  • its been maybe 4-5 weeks since I watched this! ๐Ÿ˜…๏ธ

Type Classes

  • simulacrum - typeclass library

  • provides @typeclass annotation

  • @typeclass is an annotation that provides enrichments or extension methods for an F[A]

    • ๐Ÿง™ magic with super ("scary") complicated types happen ~33:32
      • ๐Ÿ“๏ธ note that searching for Lambda in Scala will probably be painful and not provide any actual information, but also provide tons of information not-about Lambda but perhaps "lambdas" as a concept! Welcome to relying on web searches to learn about Scala!
    • more mysterious things happen with .compose
      • apparently its very powerful! it can do more than the standard library!
        • im not at all clear on what that is, but yah, sure!
          • programmers frickin' love things that are "powerful" ๐Ÿ’ช๏ธ
    • actually, there is a grok'able example @ 37:00
      • Im a believer, but this is still just a matter of faith!
    • apparently, nothing in this section is going "very deep with Functor"
      • but apparently it offers lots of things not in the standard Collections library!
        • which is what all language libraries do, so Im still not sold, at all, on why any of this is important
      • software libraries: they add more power (cool story!)
        • and I guess adding near-infinite increasing complexity somehow lets one get even more power? where is it? what is it? ๐Ÿค”๏ธ
    • author seemed like he was just right about to explain Lambda and then does the typical Scala cultural thing where he talks a lot "about" it and "around" it but doesnt at all explain what it is.
      • are Scalaists really just Daoists? โ˜ฏ๏ธ
        • the type that can be spoken is not the type!
      • holy ๐Ÿ’ฉ๏ธ my brain hurts! ๐Ÿง ๏ธ๐Ÿ˜ข๏ธ

Structural Type

  • a thing that looks like: Functor[({type l[a] = Function1[Int, a] })#l]
    • the {} imply Structural Type.
    • the #l is a Type Projection, which represents the type member l of the structural type (defined in the {})
    • this is a Type Lambda

King Projector

  • convience for anonymous Structural Types

    • see cats kind projector
      • this is where Lambda comes from!
        • ok, srsly, how useful is any of this if this much complexity is required?
          • again, WHY is any of this important at all?
          • why not juse use a tool (language) thats much simpler to explain?
    • adds support for F[X => ?] syntax
  • The Lambda keyword defines type functions

  • ๐Ÿ†’๐Ÿ†’๐Ÿ†’!

    • ๐Ÿ”š

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment