Giter VIP home page Giter VIP logo

free-monad-coproduct-example's Introduction

Free Monads with FreeK

A demonstration on how to compose different instruction sets together using Coproduct minimizing boilerplate using FreeK

Libraries used:

In the example shown here which is based off Chris Myer's talk: A Year Living Freely, we cheat and make instruction sets inherit from a common instruction set. When you do this, it becomes easy to compose instruction sets due to the common instruction set so you build a big interpreter that takes in the common instruction trait and dispatches instructions to the little interpreters. You can see an example of this in action here.

If you don't have the luxury of controlling the source of all instruction sets then you need to turn to Coproducts. Rúnar talks about this concept here. This project is a demonstration similar to Rúnar's but using FreeK's work to minimize as much boilerplate as possible.

We have two instruction sets:

  • Logging

      sealed trait LogInstruction[Result]
      case class Debug(message: String) extends LogInstruction[Unit]
      case class Info(message: String) extends LogInstruction[Unit]
      case class Warn(message: String) extends LogInstruction[Unit]
  • Greeting

      sealed trait GreetingsInstruction[Result]
      case class WhoAreYou(message: String) extends GreetingsInstruction[String]
      case object Hello extends GreetingsInstruction[Unit]
      case object Bye extends GreetingsInstruction[Unit]

We want to be able to compose instructions from both instruction sets together into a single program.

Process

Define a Coproduct that mixes the instruction sets together with the help of FreeK

import freek._
  sealed trait LogInstruction[Result]
  // ...
  
  sealed trait GreetingsInstruction[Result]
  // ...
  
  type ApplicationInstruction = LogInstruction :|: GreetingsInstruction :|: NilDSL
  val ApplicationInstruction = DSL.Make[ApplicationInstruction]

Define smart constructors that lift both your instruction sets into the Coproduct instruction set using Free

  // smart constructors
  def debug(message: String): Free[ApplicationInstruction.Cop, Unit] =
    Debug(message).freek[ApplicationInstruction]

  def info(message: String): Free[ApplicationInstruction.Cop, Unit] =
    Info(message).freek[ApplicationInstruction]

  def warn(message: String): Free[ApplicationInstruction.Cop, Unit] =
    Warn(message).freek[ApplicationInstruction]

  def whoAreYou(message: String): Free[ApplicationInstruction.Cop, String] =
    WhoAreYou(message).freek[ApplicationInstruction]

  def hello: Free[ApplicationInstruction.Cop, Unit] =
    Hello.freek[ApplicationInstruction]

  def bye: Free[ApplicationInstruction.Cop, Unit] =
    Bye.freek[ApplicationInstruction]

You can write your interpreters for each instruction set as usual

  • Logging

      // Log interpreter implementation
      val logInterpreter = new (LogInstruction ~> Id) {
        override def apply[A](fa: LogInstruction[A]): Id[A] = fa match {
          case Debug(message) =>
            println(s"DEBUG: $message")
            ()
          case Warn(message) =>
            println(s"WARN: $message")
            ()
          case Info(message) =>
            println(s"INFO: $message")
            ()
        }
      }
  • Greeting

      val greetingsInterpreters = new (GreetingsInstruction ~> Id) {
        override def apply[A](fa: GreetingsInstruction[A]): Id[A] = fa match {
          case WhoAreYou(message: String) =>
            println(message)
            val userInput = scala.io.StdIn.readLine()
            userInput
    
          case Hello =>
            println("Hello!")
            ()
    
          case Bye =>
            println("Bye!")
            ()
        }
      }

You can write your program using instructions from both instruction sets using the smart constructors. Since they have been lifted into Free of the Coproduct, you can compose instructions from both instruction sets

val instructions = for {
_   <- debug("beginning program")
_   <- hello
you <- whoAreYou("Enter your name")
_   <- info(s"Hello $you")
_   <- warn("Exiting program")
} yield ()

These are just instructions that haven't been executed yet, they just describe what we would like to do. Let's look at how we can run these instructions through an interpreter that side-effects and executes these instructions. To do this we need to compose our little interpreters together:

// Combine interpreters
val composedInterpreter = logInterpreter :&: greetingsInterpreters

Now let's run the instructions that were composed together from different instruction sets through the composed interpreter

instructions.interpret(composedInterpreter)

free-monad-coproduct-example's People

Contributors

calvinlfer avatar

Stargazers

 avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

Forkers

r-wheeler

free-monad-coproduct-example's Issues

sbt.ResolveException: unresolved dependency: com.projectseptember#freek_2.11;0.6.7: not found

can not pull this dependency.

could you please give me a hint on how to set my build.sbt for this dependency?

my build.sbt is as follow:

name := "free-monad-coproduct-example"

version := "1.0"

scalaVersion := "2.11.8"

lazy val `free-monad-coproduct-example` =
  (project in file("."))
    .settings(Seq(addCompilerPlugin("com.milessabin" % "si2712fix-plugin_2.11.8" % "1.2.0")))

resolvers += Resolver.jcenterRepo
// resolvers += Resolver.bintrayRepo("projectseptemberinc", "maven")

libraryDependencies ++= {
  Seq(
    "org.typelevel"             %% "cats"     % "0.8.1",
    "com.projectseptember"      %% "freek"    % "0.6.7"
  )
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.