Enterprise Algebras, Scala World 2016

74
ENTERPRISE ALGEBRAS Timothy Perrett Scala World 2016

Transcript of Enterprise Algebras, Scala World 2016

Page 1: Enterprise Algebras, Scala World 2016

ENTERPRISE ALGEBRASTimothy Perrett

Scala World 2016

Page 2: Enterprise Algebras, Scala World 2016

HELLO.*waves*

Page 3: Enterprise Algebras, Scala World 2016

“Tuesday”

WE’RE HIRING!

Page 4: Enterprise Algebras, Scala World 2016

WE ❤ FP.It’s not just for academics!

Page 5: Enterprise Algebras, Scala World 2016

OSS.github.com/verizon

Page 6: Enterprise Algebras, Scala World 2016

~1000 REPOS.~ 85% are Scala projects (yes, built with SBT).

Page 7: Enterprise Algebras, Scala World 2016

11 SBT PLUGINS.Doing the nasty so users don't have too.

Page 8: Enterprise Algebras, Scala World 2016

SUCH SIZE. SO WOW.Requires scaling and automation of all the things.

Page 9: Enterprise Algebras, Scala World 2016

METHOD.Constraints liberate. Liberties constrain.

Page 10: Enterprise Algebras, Scala World 2016

METHOD.One uses frameworks. Frameworks use you.

Page 11: Enterprise Algebras, Scala World 2016

Invest in the future, today.

EDUCATION.

Page 12: Enterprise Algebras, Scala World 2016

EDUCATION.

➤ Evangelize functional programming benefits to team.

➤ Users initially self-directed learning from texts and online resources.

➤ Dissemination of techniques from staff (typically the team lead or coworkers)

➤ Practice. Practice. Practice. Pull requests provide a path to learn every day.

➤ Internal courses and materials for learning more advanced FP techniques.

➤ Further practice and experience. Rinse and repeat :-)

Page 13: Enterprise Algebras, Scala World 2016

REAL-WORLD FPit actually does work in production.

Page 14: Enterprise Algebras, Scala World 2016

HLIST“seems like a good idea”

Page 15: Enterprise Algebras, Scala World 2016

HLIST.

➤ It’s THE gateway drug for advanced Scala.

➤ Easy to understand.

➤ Untold numbers of business uses.

➤ One of the most used third-party construct across our internal eco-system.

Page 16: Enterprise Algebras, Scala World 2016

FREE & COYONADA.

Page 17: Enterprise Algebras, Scala World 2016

FREE.Certainly not an exhaustive guide.

Page 18: Enterprise Algebras, Scala World 2016

sealed trait Free[F[_],A]

Page 19: Enterprise Algebras, Scala World 2016

case class Return[F[_],A](a: A) extends Free[F,A]

case class Suspend[F[_],A](s: F[Free[F, A]]) extends Free[F,A]

Page 20: Enterprise Algebras, Scala World 2016

case class Return[F[_],A](a: A) extends Free[F,A]

case class Suspend[F[_],A](s: F[Free[F, A]]) extends Free[F,A]

Page 21: Enterprise Algebras, Scala World 2016

case class Return[F[_],A](a: A) extends Free[F,A]

case class Suspend[F[_],A](s: F[Free[F, A]]) extends Free[F,A]

Page 22: Enterprise Algebras, Scala World 2016

THE YONEDA LEMMA.

Page 23: Enterprise Algebras, Scala World 2016

def apply[B](f: A => B): F[B]

Page 24: Enterprise Algebras, Scala World 2016

abstract class Yoneda[F[_], A]{ def apply[B](f: A => B): F[B]}

Page 25: Enterprise Algebras, Scala World 2016

def toYoneda[F[_] : Functor, A](fa: F[A]) = new Yoneda[F,A] { def apply[B](f: A => B) = Functor[F].map(fa)(f) }

def fromYoneda[F[_], A](yo: Yoneda[F, A]) = yo.apply(identity)

Page 26: Enterprise Algebras, Scala World 2016

SO WHAT?

Page 27: Enterprise Algebras, Scala World 2016

sealed abstract class Coyoneda[F[_], A]{ type I val fi: F[I] val k: I => A}

Page 28: Enterprise Algebras, Scala World 2016

sealed abstract class Coyoneda[F[_], A]{ type I val fi: F[I] val k: I => A}

Page 29: Enterprise Algebras, Scala World 2016

YAY!

Page 30: Enterprise Algebras, Scala World 2016

case class Suspend[F[_],A](s: F[Free[F, A]]) extends Free[F,A]

Page 31: Enterprise Algebras, Scala World 2016

type FreeC[S[_], A] = Free[({type f[x] = Coyoneda[S, x]})#f, A]

Page 32: Enterprise Algebras, Scala World 2016

NOMAD MONAD.Fun to say. Better to use.

Page 33: Enterprise Algebras, Scala World 2016

sealed abstract class SchedulerOp[A] extends Product with Serializable

Page 34: Enterprise Algebras, Scala World 2016

final case class Delete(…) extends SchedulerOp[Unit]

final case class Launch(…) extends SchedulerOp[Unit]

Page 35: Enterprise Algebras, Scala World 2016

type SchedulerF[A] = Free.FreeC[SchedulerOp, A]

Page 36: Enterprise Algebras, Scala World 2016

def delete(…): SchedulerF[Unit] = Free.liftFC(Delete(x,y))

Page 37: Enterprise Algebras, Scala World 2016

SchedulerOp ~> Task

def apply[A](s: SchedulerOp[A]): Task[A]

Page 38: Enterprise Algebras, Scala World 2016

SchedulerOp ~> Task

def apply[A](s: SchedulerOp[A]): Task[A]TESTABLE

Page 39: Enterprise Algebras, Scala World 2016

CO-PRODUCTS.

Page 40: Enterprise Algebras, Scala World 2016

for { a <- free1.bar(123) b <- free2.foo(a)} yield b

Page 41: Enterprise Algebras, Scala World 2016

for { a <- free1.bar(123) b <- free2.foo(a)} yield b

Page 42: Enterprise Algebras, Scala World 2016

final case class Coproduct[F[_], G[_], A]( run: F[A] \/ G[A])

Page 43: Enterprise Algebras, Scala World 2016

final case class Coproduct[F[_], G[_], A]( run: F[A] \/ G[A])

algebra 1

algebra 2

Page 44: Enterprise Algebras, Scala World 2016

type CombOp[A] = Coproduct[QuxOp, FooOp, A]

Page 45: Enterprise Algebras, Scala World 2016

type CombF[A] = Free.FreeC[CombOp, A]

Page 46: Enterprise Algebras, Scala World 2016

def example: CombF[String] = for { a <- useQux(1) b <- useFoo(a) } yield b

Page 47: Enterprise Algebras, Scala World 2016

def useQux(x: Int): CombF[Int] = QuxOp.gogogo(x).inject

def useFoo(x: Int): CombF[String] = FooOp.gogogo(x).inject

Page 48: Enterprise Algebras, Scala World 2016

final implicit class InjectCombF[F[_],A]( val fa: Free.FreeC[F,A])( implicit I: Inject[F, Comb.CombOp]){ def inject[G[_]] = injectFC.apply(fa)}

Page 49: Enterprise Algebras, Scala World 2016

def injectFC[F[_], G[_]](implicit I: Inject[F, G]): FreeC[F, ?] ~> FreeC[G, ?] = new (FreeC[F, ?] ~> FreeC[G, ?]) { def apply[A](fa: FreeC[F, A]): FreeC[G, A] = fa.mapSuspension[Coyoneda[G, ?]]( new (Coyoneda[F, ?] ~> Coyoneda[G, ?]) { def apply[B](fa: Coyoneda[F, B]): Coyoneda[G, B] = fa.trans(I) } ) }

sorry, there aren’t really ways to simplify this.

Page 50: Enterprise Algebras, Scala World 2016

def injectFC[F[_], G[_]](implicit I: Inject[F, G]): FreeC[F, ?] ~> FreeC[G, ?] = new (FreeC[F, ?] ~> FreeC[G, ?]) { def apply[A](fa: FreeC[F, A]): FreeC[G, A] = fa.mapSuspension[Coyoneda[G, ?]]( new (Coyoneda[F, ?] ~> Coyoneda[G, ?]) { def apply[B](fa: Coyoneda[F, B]): Coyoneda[G, B] = fa.trans(I) } ) }

transform free algebras

sorry, there aren’t really ways to simplify this.

Page 51: Enterprise Algebras, Scala World 2016

def injectFC[F[_], G[_]](implicit I: Inject[F, G]): FreeC[F, ?] ~> FreeC[G, ?] = new (FreeC[F, ?] ~> FreeC[G, ?]) { def apply[A](fa: FreeC[F, A]): FreeC[G, A] = fa.mapSuspension[Coyoneda[G, ?]]( new (Coyoneda[F, ?] ~> Coyoneda[G, ?]) { def apply[B](fa: Coyoneda[F, B]): Coyoneda[G, B] = fa.trans(I) } ) }

sorry, there aren’t really ways to simplify this.

transform coyonedas

Page 52: Enterprise Algebras, Scala World 2016

def injectFC[F[_], G[_]](implicit I: Inject[F, G]): FreeC[F, ?] ~> FreeC[G, ?] = new (FreeC[F, ?] ~> FreeC[G, ?]) { def apply[A](fa: FreeC[F, A]): FreeC[G, A] = fa.mapSuspension[Coyoneda[G, ?]]( new (Coyoneda[F, ?] ~> Coyoneda[G, ?]) { def apply[B](fa: Coyoneda[F, B]): Coyoneda[G, B] = fa.trans(I) } ) }

sorry, there aren’t really ways to simplify this.

F[A] => G[A]

Page 53: Enterprise Algebras, Scala World 2016

APPLICATION DESIGN.

Page 54: Enterprise Algebras, Scala World 2016

short-lived request response

Page 55: Enterprise Algebras, Scala World 2016

free algebra

Page 56: Enterprise Algebras, Scala World 2016

coproduct algebra

Page 57: Enterprise Algebras, Scala World 2016

alternitivly, use kleisli

Page 58: Enterprise Algebras, Scala World 2016

edge of the world

Page 59: Enterprise Algebras, Scala World 2016

long-lived stream

Page 60: Enterprise Algebras, Scala World 2016

edge of the world

Page 61: Enterprise Algebras, Scala World 2016

case GET -> Root / "v1" / "datacenters" / dcname & IsAuthenticated(session) => jsonF(Nelson.fetchDatacenterByName(dcname)){ option => option match { case Some(dc) => Ok(dc.asJson) case None => NotFound(s"datacenter '$dcname' does not exist") } }

sorry, there aren’t really ways to simplify this.

protocol specifics

Page 62: Enterprise Algebras, Scala World 2016

case GET -> Root / "v1" / "datacenters" / dcname & IsAuthenticated(session) => jsonF(Nelson.fetchDatacenterByName(dcname)){ option => option match { case Some(dc) => Ok(dc.asJson) case None => NotFound(s"datacenter '$dcname' does not exist") } }

sorry, there aren’t really ways to simplify this.

domain function

Page 63: Enterprise Algebras, Scala World 2016

BlazeBuilder .bindHttp(8080, 127.0.0.1) .mountService(service) .start // Task[Server] .run // Unit

sorry, there aren’t really ways to simplify this.

Page 64: Enterprise Algebras, Scala World 2016

APPLICATION DESIGN.

➤ All your I/O boundaries - from a systems perspective - should be a Free algebra.

➤ Database access.

➤ Network access.

➤ Any other random things you can’t sensibly reason about.

➤ Be extremely selective about the edge of the world.

➤ Understand the semantics of running your Task.

Page 65: Enterprise Algebras, Scala World 2016

TECHNICAL DEBT.

Page 66: Enterprise Algebras, Scala World 2016

TECHNICAL DEBT

➤ Accept it will happen. Minimization is the best you can hope for.

➤ Proactively push debt repositories to the leaves of your system graph.

➤ Libraries are the key battleground in debt avoidance. Choose wisely.

➤ Be consistent with different library APIs; this drives adoption and avoids accidental debt when users do the unexpected.

Page 67: Enterprise Algebras, Scala World 2016

CHALLENGES.

Page 68: Enterprise Algebras, Scala World 2016

CHALENGES.

➤ Scala is not a pure FP language.

➤ Ironically a positive and enabling thing for both beginners, and the business.

➤ Community libraries can be a mixed bag.

➤ Not everyone prioritizes maintaining binary compatible releases.

➤ Many internal forks to stabilize deps.

➤ GC is a real and present danger.

➤ Ensuring users don’t revert to Java without the semicolons is a definite and on-going challenge.

* Both fleshy and technical challenges* Generally speaking, hiring for Scala has not been a big issue.

Page 69: Enterprise Algebras, Scala World 2016

WINS.

Page 70: Enterprise Algebras, Scala World 2016

WINS.

➤ Doing FP has allowed us to recruit and retain some brilliant minds.

➤ The application of Free, Cofree, Fix and other advanced type-level paradigms make your software easier to refactor, and cheaper over time.

Page 71: Enterprise Algebras, Scala World 2016

CONCLUSIONS.

Page 72: Enterprise Algebras, Scala World 2016

CONCLUSIONS.

➤ Free & Coproduct allow us to build modular, testable, systems from discrete programs.

➤ Your program has programs.

➤ Running Scala at scale is not zero-cost. Far from it.

➤ You need expertise in the JVM to understand corner cases (happens for Java too).

➤ Purely functional stream programming currently struggles at high-load due to GC pauses.

➤ Team education is a hard problem.

➤ Don’t hire for quantity. Hire for quality and aptitude.

Page 73: Enterprise Algebras, Scala World 2016

SCALA CENTER.https://github.com/scalacenter/advisoryboard/pulls

we need the community!

Page 74: Enterprise Algebras, Scala World 2016

EOFtimperrettgithub.com/verizon