- ๐ 2019-04-19
- this spans many weeks / investment times
- ๐น Screencast: Functional Structures
- ๐จโ๐ป๐๏ธ an experienced OOP programmer learning FP and Scala
- and maybe category theory? ๐ค
- Cool REPL command:
:k TYPE
which reveals theKind
ofTYPE
๐- e.g.,
:k Int
=>A
:k List
=>F[+A]
- e.g.,
-
A Higher Kinded Type
- can not be the value of an expression.
- A
List
(alone) can not be the value of an expression. But, aList[Thinger]
can be.List
is the Higher Kinded TypeThinger
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]
, orList[Thinger]
- This is also called a Type Constructor
- i.e., the
-
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]
, theF[_]
is a Type Constructor- specifically, a Type Constructor with one argument
- the arg could be
List(1)
orSome(1)
orRight(1)
- but it can not be an
Int
/A
- but it can not be an
- 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?"
- ๐ค ๐ : if this is true, then whatabout
- it can not be called as:
bar(Some(1), List(2))
as that uses two different Proper Types (Option
andList
), and we only define one type parameter (A
)
- For type parameters:
- 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
- will have a method like:
- ๐ 7:30
- 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 contrivedFunctor
- 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
- above code is actually a
- i.e.,
- e.g., composition law states that for
F
andG
, we can compose them together as a single function, or, they can be composed piecewise through many functions
- the set of rules that define how the
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 amap()
method - as do most std lib collections (
Option
, etc)
- this implementation is simple, since
Functor[X => ?]
-> afunction1Functor
- ๐๏ธ 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 functionX => A
orX => 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 => ?]
- and fyi, a type parameter is required to define the
function1Functor.map()
can begin implementation as:def map[A, B](fa: X => A)(f: A => B): X => B =
...
oh ๐ฉ๏ธ,B
has nomap
๐ค๏ธ โ- we need to return a new function
X => B
-> the answer is compositionandThen
๐๏ธ
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?
- ๐ค๏ธ so where did
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! ๐ ๏ธ
-
simulacrum - typeclass library
-
provides
@typeclass
annotation -
@typeclass
is an annotation that provides enrichments or extension methods for anF[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-aboutLambda
but perhaps "lambdas" as a concept! Welcome to relying on web searches to learn about Scala!
- ๐๏ธ note that searching for
- 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" ๐ช๏ธ
- im not at all clear on what that is, but yah, sure!
- apparently its very powerful! it can do more than the standard library!
- 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? ๐ค๏ธ
- but apparently it offers lots of things not in the standard Collections
library!
- 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! ๐ง ๏ธ๐ข๏ธ
- are Scalaists really just Daoists? โฏ๏ธ
- ๐ง magic with super ("scary") complicated types happen ~33:32
- 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 memberl
of the structural type (defined in the{}
) - this is a Type Lambda
- the
-
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?
- ok, srsly, how useful is any of this if this much complexity is required?
- this is where
- adds support for
F[X => ?]
syntax
- see cats kind projector
-
The
Lambda
keyword defines type functions -
๐๐๐!
- ๐