1 of 101

Bake Apps

Free, Coproduct & Types,

A sip of SI2712 & Onions

2 of 101

Pascal Voitot

@mandubian

Project September Inc.

3 of 101

Building Apps

w/ Free monads?

Why do we need that?

What leads us to that?

What else?

4 of 101

Episode 1

Describe

&

Conquer

5 of 101

Surrealism

6 of 101

7 of 101

8 of 101

Math

9 of 101

Building an App

Specifications

Business Domain

Logic Code

Execution & Resources

Representation

Representation

Representation

LambdaWorld Recap

10 of 101

2 Representations - 1 Host Language

Machine World

Machine-oriented

Host Language

Human World

Description

Of Business Logic

Execution

& Resources

LambdaWorld Recap

11 of 101

Separation of Concerns

Human World

Machine World

Execution Domain 1

Execution Domain n

DSL

written in

Host Language

Execution

& Resources

Description

Of Business Logic

LambdaWorld Recap

12 of 101

Better ways of expression & control

Description

Of Business Logic

Execution

& Resources

Human World

Machine World

Immutability

Ref Transparency

Monad

...

Constraints & Logic / Type

Behaviors & laws / Typeclass

Abstraction / Higher-Kind

Flow / Monad

...

LambdaWorld Recap

MATHEMATICS

13 of 101

Mathematics is a tool to express better

our thought

LambdaWorld Recap

14 of 101

Free Monads

Is a math tool

that solves

Separation

of

Concerns

LambdaWorld Recap

15 of 101

Episode 2

16 of 101

Cook AND Develop

17 of 101

Eat

18 of 101

Junk Food isn’t good for you

19 of 101

Eat Quality Food

20 of 101

Quality means Knowledge

Study & Search all the time

Know a bit

Practice a lot

21 of 101

Tradition & Science

22 of 101

Knowledge by Tradition

23 of 101

Tradition & Experiments

24 of 101

Tradition doesn’t know why

25 of 101

Science knows why (most of times)

26 of 101

Simplicity

27 of 101

Sharpness

28 of 101

Sureness

29 of 101

ENJOY

SHARE

30 of 101

Developing is

the same

LambdaWorld Recap

31 of 101

Conclusion

LambdaWorld Recap

32 of 101

Programming & Math = Osmosis

33 of 101

Free Monads

Is a math tool

that enables

Separation

of

Concerns

LambdaWorld Recap

34 of 101

Ce ne serait pas un peu de la M…. ?

35 of 101

Specifications : BioCassoulet

36 of 101

object BioCassoulet {

def program(clientId: String, nbCans: Int) = for {

cans <- CanStock.Check(nbCans)

nb = nbCans - cans.size

cans <- if(nb > 0) {

for {

_ <- Log.Info(s"${cans.size} in stock, missing ${nb}")

bean <- GMOCorp.OrderBeans(nb)

meat <- GMOCorp.OrderMeat(nb)

fat <- ChemTrust.OrderFat(nb * 10)

e2xx <- ChemTrust.OrderPreservatives(nb * 50)

cassoulet <- NuclearCooker.CookCassoulet(meat, bean, fat, e2xx)

newCans <- CanMachine.Make(cassoulet)

} yield (cans ++ newCans)

} else {

cans

}

_ <- DeliveryService.Send(clientId, cans)

} yield (cans)

}

1/ Monadic Logic

37 of 101

object BioCassoulet {

def program(clientId: String, nbCans: Int) = for {

cans <- CanStock.Check(nbCans)

nb = nbCans - cans.size

cans <- if(nb > 0) {

for {

_ <- Log.Info(s"${cans.size} in stock, missing ${nb}")

bean <- GMOCorp.OrderBeans(nb)

meat <- GMOCorp.OrderMeat(nb)

fat <- ChemTrust.OrderFat(nb * 10)

e2xx <- ChemTrust.OrderPreservatives(nb * 50)

cassoulet <- NuclearCooker.CookCassoulet(meat, bean, fat, e2xx)

newCans <- CanMachine.Make(cassoulet)

} yield (cans ++ newCans)

} else {

cans

}

_ <- DeliveryService.Send(clientId, cans)

} yield (())

}

1/ Monadic Logic

Verb

DSL

Return Type

38 of 101

case class Beans(); case class Meat(); case class Fat();

case class Preservatives(); case class Can(); case class Cassoulet()

sealed trait Log[A]

object Log {

final case class Info(msg: String) extends Log[Unit]

final case class Error(msg: String) extends Log[Unit]

}

sealed trait CanStock[A]

object CanStock {

final case class Check(nb: Int) extends CanStock[List[Can]]

}

sealed trait GMOCorp[A]

object GMOCorp {

final case class OrderBeans(units: Int) extends GMOCorp[Beans]

final case class OrderMeat(units: Int) extends GMOCorp[Meat]

}

2/ Grammar

Verb

DSL

Return Type

39 of 101

sealed trait ChemTrust[A]

object ChemTrust {

final case class OrderFat(units: Int) extends ChemTrust[Fat]

final case class OrderPreservatives(units: Int) extends ChemTrust[Preservatives]

}

sealed trait NuclearCooker[A]

object NuclearCooker {

final case class CookCassoulet(meat: Meat, beans: Beans, fat: Fat, prez: Preservatives) extends NuclearCooker[Cassoulet]

}

sealed trait CanMachine[A]

object CanMachine {

final case class Make(cassoulet: Cassoulet) extends CanMachine[List[Can]]

}�sealed trait DeliveryService[A]

object DeliveryService {

final case class Send(clientId: String, cans: List[Can]) extends DeliveryService[Unit]

}

2/ Grammar

40 of 101

[error] /freek-sample/src/main/scala/ScalaIO.scala:57: value map is not a member of cassoulet.CanStock.Check

[error] nbCan <- CanStock.Check(nbCans)

[error]

41 of 101

I’d prefer some Monads with fava beans!

42 of 101

Hopefully, I have some Free Coyo in the fridge!

43 of 101

F[A]

=> Coyoneda[F, A] : Functor (map)

=> Free[F, A] : Monad (flatMap)

44 of 101

F[A]

=> Coyoneda[F, A] : Functor

=> Free[F, A] : Monad

// ARTIFICIAL CONSTRUCTION TO

// DELAY THE NEED OF MONADS

45 of 101

object BioCassoulet {

def program(clientId: String, nbCans: Int) = for {

cans <- CanStock.Check(nbCans) .liftFC

nb = nbCans - cans.size

cans <- if(nb > 0) {

for {

_ <- Log.Info(s"Missing $nb boxes") .liftFC

bean <- GMOCorp.OrderBeans(nbCans) .liftFC

meat <- GMOCorp.OrderMeat(nbCans) .liftFC

fat <- ChemTrust.OrderFat(nbCans * 10) .liftFC

prez <- ChemTrust.OrderPreservatives(nbCans * 50).liftFC

cassoulet <- NuclearCooker.CookCassoulet(

bean, meat, fat, prez) .liftFC

newCans <- CanMachine.Make(cassoulet) .liftFC

} yield (cans ++ newCans)

} else {

Free.pure(cans)

}

_ <- DeliveryService.Send(cans) .liftFC

} yield (())

}

46 of 101

object BioCassoulet {

def program(clientId: String, nbCans: Int) = for {

cans <- CanStock.Check(nbCans).liftFC //=> Free[CanStock, List[Can]]

nb = nbCans - cans.size

cans <- if(nb > 0) {

for {

_ <- Log.Info(s"Missing $nb boxes").liftFC //=> Free[Log, Unit]

bean <- GMOCorp.OrderBeans(nbCans).liftFC //=> Free[GMOCorp, Beans]

meat <- GMOCorp.OrderMeat(nbCans).liftFC //=> Free[GMOCorp, Meat]

fat <- ChemTrust.OrderFat(nbCans * 10).liftFC //=> Free[ChemTrust, Fat]

prez <- ChemTrust.OrderPreservatives(nbCans * 50).liftFC

cassoulet <- NuclearCooker.CookCassoulet(bean, meat, fat, prez).liftFC

newCans <- CanMachine.Make(cassoulet).liftFC

} yield (cans ++ newCans)

} else {

Free.pure(cans)

}

_ <- DeliveryService.Send(cans).liftFC

} yield (())

}

47 of 101

Free[CanStock, ?]

Free[Log, ?]

Free[GMOCorp, ?]

Free[ChemTrust, ?]

Free[NuclearService, ?]

Free[CanMachine, ?]

Free[DeliveryService, ?]

Free[

CanStock OR

Log OR

GMOCorp OR

ChemTrust OR

NuclearService OR

CanMachine OR

DeliveryService OR

, ?]

48 of 101

type PRG =

Log OR

CanStock OR

GMOCorp OR

ChemTrust OR

NuclearCooker OR

CanMachine OR

DeliveryService

49 of 101

FREEK

IT

50 of 101

type PRG =

Log :|:

CanStock :|:

GMOCorp :|:

ChemTrust :|:

NuclearCooker :|:

CanMachine :|:

DeliveryService :|:

NilDSL

/*Artificial value to convince scalac about types*/

val PRG = DSL.Make[PRG]

51 of 101

def program(clientId: String, nbCans: Int) /*: Free[PRG.Cop, List[Can]] */ = for {

cans <- CanStock.Check(nbCans) .freek[PRG]

nb = nbCans - cans.size

cans <- if(nb > 0) {

for {

_ <- Log.Info(s"${cans.size} in stock, missing $nb").freek[PRG]

bean <- GMOCorp.OrderBeans(nbCans) .freek[PRG]

meat <- GMOCorp.OrderMeat(nbCans) .freek[PRG]

fat <- ChemTrust.OrderFat(nbCans * 10) .freek[PRG]

e2xx <- ChemTrust.OrderPreservatives(nbCans * 50) .freek[PRG]

cassoulet <- NuclearCooker.CookCassoulet(meat,bean,fat,e2xx).freek[PRG]

newCans <- CanMachine.Make(cassoulet) .freek[PRG]

} yield (cans ++ newCans)

} else {

Free.pure[PRG.Cop, List[Can]](cans)

}

_ <- DeliveryService.Send(clientId, cans) .freek[PRG]

} yield (cans)

52 of 101

Freek

Use Free programs to describe your logic & separate description from execution

Use existing Free (cats)

Helpers to Combine DSL (HigherKind CoproductK) & hide type boilerplate

53 of 101

Interpret Free[DSL, ?] to runtime

DSL

Monadic

Domain of Execution

M

FS2 Task

Monix Task

Scalaz Task

(even Future)

...

Machine Language

Interpret/foldMap

Compile

Natural

Transformation

DSL[_] ~> M[_]

Compiler

Scalac

Note: DSL can be translated into an effect but not necessarily

54 of 101

val interpreter: DSL ~> M = ???

val freeDSL: Free[DSL, A] = ???

val ma: M[A] = freeDSL.foldMap(interpreter)

55 of 101

val Log2Id = new (Log ~> Id) {

def apply[A](l: Log[A]): Id[A] = l match {

case Log.Info(msg) => println(s"[info] $msg")

case Log.Error(msg) => println(s"[error] $msg")

}

}

import fs2.Task

val Log2Task = new (Log ~> Task) {

def apply[A](l: Log[A]): Task[A] = l match {

case Log.Info(msg) => Task.now(println(s"[info] $msg"))

case Log.Error(msg) => Task.now(println(s"[error] $msg"))

}

}

56 of 101

// Postgres Interpreter

class CanPostgresRepository {

def check(nb: Int): Task[List[Can]] = ???

}

class CanStock2Task(

repo: CanPostgresRepository

) extends (CanStock ~> Task) {

def apply[A](l: CanStock[A]): Task[A] = l match {

case CanStock.Check(nb) => repo.check(nb)

}

}

val canPostgresRepo: CanPostgresRepository = ???

val canStock2Task = new CanStock2Task(canPostgresRepo)

57 of 101

// Mock�object CanStockAlwaysInStock extends (CanStock ~> Id) {

def mockCans(nb: Int): List[Can] = ???

def apply[A](l: CanStock[A]): Id[A] = l match {

case CanStock.Check(nb) => mockCans(nb)

}

}

58 of 101

When there is for 1, there is for N

59 of 101

Combining DSL & Interpreters

Log

Monad M

CanStock

OR

Log ~> M

CanStock ~> M

AND

GMOCorp

OR

ChemTrust

OR

NuclearCooker

OR

CanMachine

OR

GMOCorp ~> M

AND

ChemTrust ~> M

AND

NuclearCooker ~> M

AND

CanMachine ~> M

AND

interpret

Coproduct

Product

PRG.Cop

PRG.Cop ~> M

DeliverService

DeliverService ~> M

OR

AND

60 of 101

val Log2Task: Log ~> Task = ???

val CanStock2Task: CanStock ~> Task = ???

val GMOCorp2Task: GMOCorp ~> Task = ???

val ChemTrust2Task: ChemTrust ~> Task = ???

val NuclearCooker2Task: NuclearCooker ~> Task = ???

val CanMachine2Task: CanMachine ~> Task = ???

val DeliveryService2Task: DeliveryService ~> Task = ???

val interpreters /*:PRG.Cop ~> Task */ =

Log2Task :&:

CanStock2Task :&:

GMOCorp2Task :&:

ChemTrust2Task :&:

NuclearCooker2Task :&:

CanMachine2Task :&:

DeliveryService2Task

61 of 101

val nb: Int = ???

val clientId: String = ???

val program = BioCassoulet.program(clientId, nb)

val res: Task[List[Can]] = program.interpret(interpreters)

// REST OF

// CODE IS

// BORING

62 of 101

Free project

1 compile unit

grammar.scala

+

freeprogram.scala

1 compile unit

interpreters.scala

1 compile unit

run.scala

63 of 101

val interpreters =

//Log2Task :&:

CanStock2Task :&:

GMOCorp2Task :&:

ChemTrust2Task :&:

NuclearCooker2Task :&:

CanMachine2Task :&:

DeliveryService2Task

[info] Compiling 1 Scala source to /freek-sample/target/scala-2.11/classes...

[error] /freek-sample/src/main/scala/ScalaIO.scala:203: could not find implicit value for parameter sub: freek.SubCop[[A]freek.AppendK[[β]freek.In1[cassoulet.Log,β],[γ]freek.AppendK[[A]freek.In3[cassoulet.CanStock,cassoulet.GMOCorp,cassoulet.ChemTrust,A],[A]freek.In3[cassoulet.NuclearCooker,cassoulet.CanMachine,cassoulet.DeliveryService,A],γ],A],[A]freek.AppendK[[δ]freek.In3[cassoulet.CanStock,cassoulet.GMOCorp,cassoulet.ChemTrust,δ],[A]freek.In3[cassoulet.NuclearCooker,cassoulet.CanMachine,cassoulet.DeliveryService,A],A]]

[error] val res: Task[List[Can]] = program.interpret(interpreters)

64 of 101

Scalac & your compile errors, I still have a few fava in the fridge

65 of 101

I should have taken more care on return types

66 of 101

trait OrderError

sealed trait GMOCorp[A]

object GMOCorp {

final case class OrderBeans(units: Int) extends GMOCorp[Xor[OrderError, Beans]]

final case class OrderMeat(units: Int) extends GMOCorp[Xor[OrderError, Meat]]

}

sealed trait ChemTrust[A]

object ChemTrust {

final case class OrderFat(units: Int) extends ChemTrust[Xor[OrderError, Fat]]

final case class OrderPreservatives(units: Int) extends ChemTrust[Xor[OrderError, Preservatives]]

}

67 of 101

sealed trait NuclearCooker[A]

object NuclearCooker {

final case class CookCassoulet(meat: Meat, beans: Beans, fat: Fat, prez: Preservatives) extends NuclearCooker[Option[Cassoulet]]

}

trait DeliveryError

sealed trait DeliveryService[A]

object DeliveryService {

final case class Send(clientId: String, cans: List[Can]) extends DeliveryService[Xor[DeliveryError, Unit]]

}

68 of 101

[error] /freek-sample/src/main/scala/ScalaIO2.scala:149: type mismatch;

[error] found : cats.data.Xor[cassoulet2.OrderError,cassoulet2.Meat]

[error] required: cassoulet2.Meat

[error] cassoulet <- NuclearCooker.CookCassoulet(meat, bean, fat, e2xx).freek[PRG]

[error] ^

[error] /freek-sample/src/main/scala/ScalaIO2.scala:149: type mismatch;

[error] found : cats.data.Xor[cassoulet2.OrderError,cassoulet2.Beans]

[error] required: cassoulet2.Beans

[error] cassoulet <- NuclearCooker.CookCassoulet(meat, bean, fat, e2xx).freek[PRG]

[error]

69 of 101

Classic: Monad Transformers?

OptionT, XorT, EitherT,

ListT (not in cats)

...

70 of 101

Freek Onion

Stacks of Traversable Monads

Controlled by Scala path-dependent types

Helpers to manipulate

Level in the stack

71 of 101

Anatomy of an Onion

Bulb

Bulb

Bulb

Bulb

Xor[OrderError, ?]

Option

type O =

Xor[DeliveryError, ?] :&:

Option :&:

Xor[OrderError, ?] :&:

Bulb

Monad

Traversable

Xor[DeliveryError, ?]

Xor[DeliveryError, Option[Xor[OrderError, ?]]]

72 of 101

Onion lifting

Option[A]

=> Option[Xor[OrderError, A]] (Monad/Traversable)

=> Xor[DeliveryError, Option[Xor[OrderError, A]]] (Monad)

73 of 101

Free + Onion

type PRG = Log :|: CanStock :|: GMOCorp :|: ChemTrust :|: NuclearCooker :|: CanMachine :|: DeliveryService :|: NilDSL

val PRG = DSL.Make[PRG]

import cats.instances.option._

type O = Xor[DeliveryError, ?] :&:

Option :&:

Xor[OrderError, ?] :&:

Bulb

74 of 101

// Free[PRG.Cop, Xor[DeliveryError, Option[Xor[OrderError, List[Can]]]]]

def program(clientId: String, nbCans: Int) /*: Free[PRG.Cop, O#Layers[List[Can]]]*/ = for {

cans <- CanStock.Check(nbCans).freek[PRG].onion[O]

nb = nbCans - cans.size

cans <- if(nb > 0) {

for {

_ <- Log.Info(s"...").freek[PRG].onionT[O]

bean <- GMOCorp.OrderBeans(nb).freek[PRG].onionT[O]

meat <- GMOCorp.OrderMeat(nb).freek[PRG].onionT[O]

fat <- ChemTrust.OrderFat(nb * 10).freek[PRG].onionT[O]

e2xx <- ChemTrust.OrderPreservatives(nb * 50).freek[PRG].onionT[O]

cassoulet <- NuclearCooker.CookCassoulet(...).freek[PRG].onionT[O]

newCans <- CanMachine.Make(cassoulet).freek[PRG].onion[O]

} yield (cans ++ newCans)

} else Free.pure[PRG.Cop, List[Can]](cans).onion[O]

_ <- DeliveryService.Send(clientId, cans).freek[PRG].onionT[O]

} yield (cans).value

75 of 101

Freek Status

v0.6.1 for cats (0.7.0)

Used in production at Project September

Requires SI2712 patch

Scala 2.11.9 / 2.12 - Typelevel Scala

Kind projector is welcome

76 of 101

Free Reminders

Free is just a trick to delay need of a “true” Monad

Still targets (single) monadic execution domain

77 of 101

Limits

Compiling Duration when combining lots of DSL

Garbage Collection can be an issue for intensive calls or recursive Free programs

78 of 101

Limits

Limits not so critical in real-life

Free/Cop Wrapping cost << IO cost

Just be careful if you need ultra high-perf

79 of 101

Cost of Free

def doit1(): Future[Unit] = Future.successful(())

def doit2(): Future[Unit] = Future.successful(())

def doit3(): Future[Unit] = Future.successful(())

@Benchmark

def run() = {

Await.result(for {

_ <- doit1()

_ <- doit2()

_ <- doit3()

} yield (()), 2.minutes)

}

80 of 101

def prg: Free[PRG.Cop, Unit] = for {

_ <- Doit1F.freek[PRG]

_ <- Doit2F.freek[PRG]

_ <- Doit3F.freek[PRG]

} yield (())

val doit3? = new (Doit3? ~> Future) {

def apply[A](d: Doit3?[A]) = d match {

case Doit3? => Future.successful(())

}

}

val interpreter = doit1I :&: doit2I :&: doit3I

@Benchmark

def run() = Await.result(DoitF.prg.interpret(interpreter), 2.minutes)

81 of 101

# JMH

# Run complete. Total time: 00:00:30

FUTURE FREE

Benchmark Score Score Units

Main.run 65931.004 30271.182 ops/s

Main.run:·gc.alloc.rate 54.343 148.796 MB/sec

Main.run:·gc.alloc.rate.norm 863.971 5155.181 B/op

Main.run:·gc.churn.PS_Eden_Space 55.574 150.883 MB/sec

Main.run:·gc.churn.PS_Eden_Space.norm 896.169 5213.585 B/op

Main.run:·gc.churn.PS_Survivor_Space 0.036 0.122 MB/sec

Main.run:·gc.churn.PS_Survivor_Space.norm 0.600 4.277 B/op

Main.run:·gc.count 23.000 93.000 counts

Main.run:·gc.time 12.000 52.000 ms

82 of 101

Huge Free heats the Planet

var i = 0; var f = DoitF.prg

while(i < 10000000) {

f = f.flatMap { _ => DoitF.prg }

i = i + 1;

}

println(“launch”)

f.interpret(interpreter)

}

// “Launch” appears fast (3/4 sec)

// Most of time (>2mn) is spent in interpreting

83 of 101

84 of 101

Runtime Optimizations

Other Free representations

  • Right association (in cats & scalaz)
  • O(1) appending & observation (Reflection without remorse & Freer monads - used in Scala Eff)

Eliminate Free structures at compile-time (macro, plugin)…

  • FreeAp could be possible
  • FreeMonad muchmuch harder (do we want it?)

85 of 101

Compile Optim

Scalac implicit resolution

by induction

is not optimized

at all

86 of 101

Improve Structure for Compiling

CoproductK B-Tree structure (in freek v0.6.0)

F1[_]

F2[_]

F3[_]

F4[_]

AppendK

F1[_] | F2[_] | F3[_]

F4[_]

Reduce height

from n to log(n)

87 of 101

Improve Structure for Compiling

Freek v0.4.3

13 DSL => 20s

20 DSL => never-ends

Freek v0.6.1

13 DSL => 4s

20 DSL => 9s

88 of 101

I’ll solve scalac implicit induction

soon!!!

89 of 101

90 of 101

Future?

Free allows is a big plus to better express our intents

Free requires boilerplate in host language

Free is a step to something further

Other models (MTL, finally tagless, object algebra, van laarhoven etc...)

Effect-oriented models (FreerMonad, ScalaEff, Haskell extensible effects, Idris Eff, etc...)

91 of 101

Future?

Better support of DSLs & Free in host languages ?

Compiler Extension

Macro

Language Extension

Better host language & runtimes ?

Type-dependent, nearer Calculus-theory, …

92 of 101

Time Left ?

93 of 101

Freekit

Writing .freek[PRG]

hurts my little hands...

94 of 101

object program extends Freekit(PRG) {

def apply(clientId: String, nbCans: Int) = for {

cans <- CanStock.Check(nbCans)

nb = nbCans - cans.size

cans <- if(nb > 0) {

for {

_ <- Log.Info(s"Only ${cans.size} in stock, missing ${nb} boxes")

bean <- GMOCorp.OrderBeans(nb)

meat <- GMOCorp.OrderMeat(nb)

fat <- ChemTrust.OrderFat(nb * 10)

e2xx <- ChemTrust.OrderPreservatives(nb * 50)

cassoulet <- NuclearCooker.CookCassoulet(meat, bean, fat, e2xx)

newCans <- CanMachine.Make(cassoulet)

} yield (cans ++ newCans)

} else {

Free.pure[PRG.Cop, List[Can]](cans)

}

_ <- DeliveryService.Send(clientId, cans)

} yield (())

}

NEW in

0.6.1/2

95 of 101

Free Program Combining

object Program1 {

type PRG = ...

val PRG = DSL.Make[PRG]

def program =

for { ... } yield (...)

}

object Program2 {

type PRG = ...

val PRG = DSL.Make[PRG]

def program =

for { ... } yield (...)

}

object Program {

type PRG = Program1.PRG :||: Program2.PRG

val PRG = DSL.Make[PRG]

def program = for {

a <- Program1.program

b <- Program2.program

} yield (...)

}

val interpreter =

interpreter1 :&&: interpreter2

96 of 101

Free Extension

object Program1 {

sealed trait Foo1[A]

final case class Foo11(s: Int) extends Foo1[String]

sealed trait Foo2[A]

final case class Foo21(s: String) extends Foo2[Int]

type PRG = Foo1 :|: Foo2 :|: NilDSL

val PRG = DSL.Make[PRG]

val program = for {

s <- Foo11(5).freek[PRG]

i <- Foo21(s).freek[PRG]

} yield (i)

}

NEW in

0.6.1

97 of 101

object Program2 {

sealed trait Foo3[A]

case class Foo31[A](foo11: Foo1[A]) extends Foo3[A]

case class Foo32(i: Int) extends Foo3[String]

type PRG = Foo3 :|: Foo2 :|: NilDSL

val PRG = DSL.Make[PRG]

// CopKNat[Program.PRG.Cop] = Program.PRG.Cop ~> Program.PRG.Cop

// Program.PRG.Cop ~> Program2.PRG.Cop

val copknat = CopKNat[Program.PRG.Cop].replace(

new (Foo1 ~> Foo3) {

def apply[A](foo1: Foo1[A]): Foo3[A] = Foo31(foo1)

}

)

val program = for {

i <- Program.program.compile(copknat)

s <- Foo32(i).freek[PRG]

} yield (s)

}

NEW in

0.6.1

98 of 101

Free Program Transpiling

val prg: Free[DSL1 :|: DSL2 :|: DSL3 :|: FXNil] = ???

val transpiler =

CopKNat[DSL1 :|: DSL2 :|: DSL3 :|: FXNil]

.replace(DSL2 ~> Free[DSL4 :|: DSL5 :|: DSL6 :|: FXNil])

val prg2: Free[DSL1 :|: DSL4 :|: DSL5 :|: DSL6 :|: DSL3 :|: FXNil] =

prg.transpile(transpiler)

DSL1 DSL1 DSL1

Free [ DSL2 ] => Free [ Free [ DSL4 :|: DSL5 :|: DSL6 ] ] => Free [ DSL4 ]

DSL3 DSL3 DSL5

DSL6

DSL3

99 of 101

Free as a Precepte (CC @skaalf)

type Step[F[_], S, A] = S => (S, F[A])

type PrecepteLike[F[_], S, A] = Free[Step[F, S, ?], A]

NEW in

0.6.x

100 of 101

Thank you

Pascal Voitot

@mandubian

Freek github

https://github.com/

ProjectSeptemberInc/

freek

101 of 101