Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
  • Sep 05 2019 14:43
    @typelevel-bot banned @jdegoes
  • Jan 31 2019 21:17
    codecov-io commented #484
  • Jan 31 2019 21:08
    scala-steward opened #484
  • Jan 31 2019 18:19
    andywhite37 commented #189
  • Jan 31 2019 02:41
    kamilongus starred typelevel/cats-effect
  • Jan 30 2019 00:01
    codecov-io commented #483
  • Jan 29 2019 23:51
    deniszjukow opened #483
  • Jan 29 2019 23:37
  • Jan 29 2019 23:22
  • Jan 29 2019 20:26
    Rui-L starred typelevel/cats-effect
  • Jan 29 2019 18:01
    jdegoes commented #480
  • Jan 29 2019 17:04
    thomaav starred typelevel/cats-effect
  • Jan 28 2019 17:43
    asachdeva starred typelevel/cats-effect
  • Jan 28 2019 07:12
    alexandru commented #480
  • Jan 28 2019 05:45
    codecov-io commented #482
  • Jan 28 2019 05:35
    daron666 opened #482
  • Jan 27 2019 13:56
    codecov-io commented #481
  • Jan 27 2019 13:46
    lrodero opened #481
  • Jan 27 2019 05:47
    codecov-io commented #460
  • Jan 27 2019 05:37
    codecov-io commented #460
Steven Fines
@sfines-clgx
sure
Fabio Labella
@SystemFw
so let's say you have def length(l: List[Int]): Int and def length(l: List[String]): String
you can represent both as def length[A](l: List[A]): Int
this is called parametric polymorphism ("generics")
hopefully familiar
Steven Fines
@sfines-clgx
right, and that part I get pretty well since I learned it way back in the C++ days
Fabio Labella
@SystemFw
but let's say you have
def length[A](l: List[A]): Int
def length[A](l: Tree[A]): Int
heh, templates in C++ actually work differently, although they are used for the same things
but anyway
can you represent that as one type?
Steven Fines
@sfines-clgx
yeah, type erasure and all that
Fabio Labella
@SystemFw
as it turns out, you cannot, in most languages
including Java, Rust, etc
you can in Scala, and Haskell
Steven Fines
@sfines-clgx
in that case it would have to be length[F[_]]... yeah?
Fabio Labella
@SystemFw
def length[F[_], A]](l: F[A]): Int
this is called higher-kinded parametric polymorphism
or in short (and a slight abuse of notation): "higher kinds"
works mostly in conjunction with typeclasses
Steven Fines
@sfines-clgx
the best notation is abused notation
Fabio Labella
@SystemFw
def length[F[_]: Foldable, A]](l: F[A]): Int
that's the real type
so these are the mechanics basically
Steven Fines
@sfines-clgx
the cats Foldable typeclass
Fabio Labella
@SystemFw
yep
trait Foldable[F[_]] { 
  ...
now, on to the pattern
this pattern (which I'm about to show) is part of a class of encodings called final tagless encodings
Steven Fines
@sfines-clgx
ok
Fabio Labella
@SystemFw
so you hear referred as "final tagless"
I have a long explanation on what final tagless means in general, you can look it up at https://systemfw.org/writings
so I'll just explain the actual pattern we use
Steven Fines
@sfines-clgx
bookmarked
Fabio Labella
@SystemFw
which could call "capability traits" or "capability classes" or whatever
the basic idea is that the types that we use for effects, have shape F[_]
this includes IO, primarily
but monad transformer as well
the idea is akin to interface and implementation
Steven Fines
@sfines-clgx
alright
Fabio Labella
@SystemFw
when you have things like
def print(s: String): IO[Unit]
def read: IO[String]

def echo: IO[Unit] = read.flatMap(print)
you want to:
  • describe the domain of effects (just like you use data types to describe the domain of data)
  • have a testable/swappable implementation
  • (additionally, sometimes) increase the generality of echo to be used with many different effects (like a traced IO effect)
and you do it like that
Steven Fines
@sfines-clgx
you would have a trait that defines those in terms of expecting some manner of effect type
Fabio Labella
@SystemFw
trait Console[F[_]] {
  def print(s: String): F[Unit]
  def read: F[String]
}
object Console {
  def apply[F[_]](implicit ev: Console[F]) = ev

  def std: Console[IO] = new Console[IO] {
     def print(s: String): IO[Unit] = ...
     def read: IO[String] = ...
  }
}
then your "programs", like echo are written as
def echo[F[_]: Console: Monad]: F[Unit] = Console[F].read.flatMap(Console[F].print)
Steven Fines
@sfines-clgx
ok
Fabio Labella
@SystemFw
this is achieves all thee goals above, and it composes nicely with other things
e.g. if you have def fetch[F[_]: Cache]: F[User]
you might use them both and get def logic[F[_]: Console: Cache]