foo[T: ClassTag]and want to specialize it dynamically to call
bar[T <: Bar]if it knows it can; the proper way would be to introduce a typeclass I know, but it's so much boilerplate
lift is just another name for
map, with a slightly rearranged signature.
map normally looks like
def map(fa: F[A])(f: A => B): F[B]. If you rearrange the arguments you get
def lift(f: A => B): F[A] => F[B], so you can look at
Functor as the api for lifting functions of one argument in
F. This extends to Applicative, which lets you lift functions of multiple arguments into
F, and ultimately into
Monad, which gives you the ability to change the structure of one computation based on the result of another.
et's say I have a method which returns optional if I want to get the value inside optional should I use lifting functions
yeah, pretty much. You should just
flatMap) and transform the
Option that way. I don't particularly like saying "the value inside the
F" because it's misleading in the long run, but it's a decent approximation at first
Deserializer[A](run: Array[Byte] => A)
A => Bgiving us a
but basically it starts from thinking about what types are for (and not what they are against, to quote Connor McBride), to the fact that they are given meaning and semantics not by their underlying representation, but by the operations you define on them (let's say just functions for now).
The second step would be defining algebras as specifications of part of the behaviour of a data type. This works really well with typeclasses, which give you an
has a, rather than the is a relationship you typically get from OO style interfaces. The result of this thought process is that given a datatype, part of what it can do is specified by operations that are unique to that type, and part by algebras (i.e. Monoid, Functor, and so on). This kind of reasoning can be explained with simple types and lower kinded typeclasses only.
Then, you move on to explaining how types of higher kind can be used not just for containers, but for computations as well: List and Option are interesting because they can be viewed both ways, but there are somethings that really only make sense as computations (like State or IO).
At this point you can actually explain F-A-M as algebras that specify part of the behaviour of higher kinded types:
The final bit is learning how to operate on types based on their algebras and operations only, while being agnostic to their representation (e.g avoiding pattern matching on
Option): this is propedeutic to learning about types which either have an opaque representation (
IO) or a very complex one (
fs2.Stream). It also means that you can tackle a new library which exposes different types, and basically know most of what you need to do to use it once you know which algebras it forms (e.g. doobie ConnectionIO).
I guess the main problem with this approach is that it requires some upfront motivation, and ideally a mentor to give you clear explanations and "unstuck" you along the way