All Projects → AdrielC → free-arrow

AdrielC / free-arrow

Licence: other
Implementation of the Free Arrow in Scala and other helpful tools for working with Arrows

Programming Languages

scala
5932 projects

Projects that are alternatives of or similar to free-arrow

functional-structures-refactoring-kata
Starting code and proposed solution for Functional Structures Refactoring Kata
Stars: ✭ 31 (+121.43%)
Mutual labels:  monad, category-theory, applicative
Monadless
Syntactic sugar for monad composition in Scala
Stars: ✭ 231 (+1550%)
Mutual labels:  cats, monad
Cats Stm
An STM implementation for Cats Effect
Stars: ✭ 106 (+657.14%)
Mutual labels:  cats, monad
elixir-control
An exploratory look into functors, applicatives, and monads for Elixir
Stars: ✭ 21 (+50%)
Mutual labels:  monad, applicative
Fp Core.rs
A library for functional programming in Rust
Stars: ✭ 772 (+5414.29%)
Mutual labels:  monad, category-theory
Functional Examples
Examples with Functional JavaScript, following Professor Frisby's course
Stars: ✭ 179 (+1178.57%)
Mutual labels:  monad, category-theory
Cats Mtl
cats transformer type classes.
Stars: ✭ 238 (+1600%)
Mutual labels:  cats, monad
Fluokitten
Category theory concepts in Clojure - Functors, Applicatives, Monads, Monoids and more.
Stars: ✭ 408 (+2814.29%)
Mutual labels:  monad, category-theory
classy-optics
🔎 Source code shown at my talks at Scale by the Bay 2018 and Scalar 2019
Stars: ✭ 25 (+78.57%)
Mutual labels:  cats, zio
tutorials
🎥 Source code of the examples shown in the video tutorials
Stars: ✭ 18 (+28.57%)
Mutual labels:  cats, zio
freecli
Command line parsing library using Free Applicative
Stars: ✭ 29 (+107.14%)
Mutual labels:  cats, free
Ltupatternfactory
Lambda the ultimate Pattern Factory: FP, Haskell, Typeclassopedia vs Software Design Patterns
Stars: ✭ 735 (+5150%)
Mutual labels:  monad, category-theory
Category Theory
An axiom-free formalization of category theory in Coq for personal study and practical work
Stars: ✭ 562 (+3914.29%)
Mutual labels:  monad, category-theory
Bastet
A ReasonML/Ocaml library for category theory and abstract algebra
Stars: ✭ 200 (+1328.57%)
Mutual labels:  monad, category-theory
Bow
🏹 Bow is a cross-platform library for Typed Functional Programming in Swift
Stars: ✭ 538 (+3742.86%)
Mutual labels:  monad, category-theory
mercator
Automatic typeclass-based abstraction over monad-like types
Stars: ✭ 54 (+285.71%)
Mutual labels:  monad, category-theory
Language Ext
C# functional language extensions - a base class library for functional programming
Stars: ✭ 3,964 (+28214.29%)
Mutual labels:  monad, applicative
Fp Resources
Functional programming great resources
Stars: ✭ 369 (+2535.71%)
Mutual labels:  monad, category-theory
http4s-poc-api
POC: http4s http api on zio
Stars: ✭ 34 (+142.86%)
Mutual labels:  cats, zio
bfect
Some bifunctor IO type classes
Stars: ✭ 19 (+35.71%)
Mutual labels:  cats, zio

Free Arrow

CircleCI codecov

Implementation of the Free Arrow in Scala and other helpful tools for working with Arrows

Based on the paper Generalizing Monads to Arrows

Use Free Arrow to build a computation graph that can be interpreted to different execution contexts and reused to create more complex flows from smaller ones. Typically the Free Arrow (FreeA) is used to compose values of some embedded DSL into a flow-like computation graph.

The primary motivation for using FreeA is to decouple the construction of a program from its interpretation, enabling the following features:

  • Create reusable and modular components to build complex programs from simpler ones
  • Change the program's interpretation without altering its structure
  • Statically introspect the graph to describe the flow
  • Optimize/rewrite a program using results from its analysis

An example use case may be creating a data processing flow which can be used both as a stand alone function for single data points or as a stream processor. With Free Arrow, you would write the flow logic once, and have two interpreters; one for a pure function and a second for the stream processor.

Example

First define a minmal set of operations (algebra)

sealed trait ConsoleOp[A, B] // Represents some console operation that takes an `A` and outputs `B`
case object GetLine                                 extends ConsoleOp[Unit, String]
case object PutLine                                 extends ConsoleOp[String, Unit]
case class Prompt(message: String)                  extends ConsoleOp[Unit, Unit]
case class Dictionary(dict: Map[String, String])    extends ConsoleOp[String, Option[String]]

Then define smart constructors to lift your algebra into the FreeArrow

import FreeArrow.liftK // for lifting `ConsoleOp` into `FreeA`

val getLine = liftK(GetLine)
val putLine = liftK(PutLine)
def prompt(message: String) = liftK(Prompt(message))
def dictionary(entry: (String, String)*) = liftK(Dictionary(entry.toMap))

Construct a program from your free operations

val translator = 
    prompt("Hello") >>>
    prompt("Enter an English word to translate") >>>
    getLine >>| ( // dead end, return output of `getLine` after the following
        putLine.lmap("Translating " + (_: String)) >>>
        prompt("...").rmap(_ => Thread.sleep(1000)).loopN(3)
      ) >>>
    dictionary(
      "apple" -> "manzana",
      "blue" -> "azul",
      "hello" -> "hola",
      "goodbye" -> "adios")
      .rmap(_.getOrElse("I don't know that one")) >>>
    putLine

Write an interpreter to do something useful with your program

val functionInterpreter: ConsoleOp ~~> Function1 = new (ConsoleOp ~~> Function1) {
  override def apply[A, B](f: ConsoleOp[A, B]): A => B = f match {
    case Prompt(message) => _ => println(message)
    case GetLine => _ => StdIn.readLine()
    case PutLine => println
    case Dictionary(dict) => dict.get
  }
}

Interpret and run your program

val program = translator.foldMap(functionInterpreter)

program(())

// Hello
// Enter an English word to translate
// Translating hello
// ...
// ...
// ...
// hola

Here's an example of introspecting the FreeArrow program to count the number of getLines used

import cats.implicits._

val numGets = translator.analyze(new (ConsoleOp ~>| Int) {
  def apply[A, B](f: ConsoleOp[A, B]): Int = f match {
    case GetLine => 1
    case _ => 0
  }
})

It is also possible to generate documentation from your free program. Here is the output of an interpreter that draws a computation graph.

translator

Other Features:

FreeA is implemented as a sealed trait that supports operations from several levels of the Arrow typeclass hierarchy.

The typeclass required to run/interpret a FreeA is inferred by the operations used to build it. Only the most general and least powerful Arrow subtype will be required.

val unit: FreeA[Arrow,      Nothing, Unit, Unit]                = FreeA.id[Unit]

val ar: FreeA[Arrow,        Nothing, Unit, Unit]                = unit >>> unit

val ac: FreeA[ArrowChoice,  Nothing, Either[Unit, Unit], Unit]  = ar ||| ar

val az: FreeA[ArrowZero,    Nothing, Unit, Unit]                = ar >>> zeroArrow[Unit, Unit]

val ap: FreeA[ArrowPlus,    Nothing, Unit, Unit]                = az <+> ar <+> ar

// val run = ap.foldMap(BiFunctionK.id[Function1]) // wont compile, 
// `Function1` does not have an ArrowPlus instance

val run = ap.foldMap(BiFunctionK.id[Function1].kleisli[List]) // compiles
// Any Kleisli[M, A, B] that has a Monad[M] and MonoidK[M] has an ArrowPlus[Kleisli[M, ?, ?]] instance

run(()) // List((), ())

Different DSLs and their interpreters can be composed together in FreeA using FreeA.inl/FreeA.inr and the BiFunctionK.or combinators respectively.

  • e.g. A FreeA[R, ConsoleOp, A, B] and a FreeA[R, MathOp, A, B] can be combined to a EitherFreeA[R, ConsoleOp, MathOp, A, B]

Type Classes Supported by FreeA

type-classes

Free Comparison

As Free constructions go, here's how FreeA sits on the spectrum of power and expressiveness:

(Co)Yoneda < Free Applicative < Free Arrow < Free Monad

Free Arrow has both the static introspection of the Free Applicative and the sequencing capability of the Free Monad

FreeArrow supports both sequencing like FreeMonad and static analysis of the free structure like FreeApplicative. This allows you to write expressive programs that can be introspected and optimized for further sequential composition

Credits

Based on the talks Beyond Free Monads and Blazing Fast, Pure Effects without Monads by John A De Goes

The ZIO arrow module has been adapted from the work in https://github.com/zio-crew/zio-arrow. Only main difference that the code in this repo has is that Impure function compisition is made stack safe.

Usage

Add this to your build.sbt:

libraryDependencies += "com.adrielc" %% "free-arrow" % "<version>"

Cross-builds are available for Scala 2.12.10 and 2.13.1.

Community

Any contribution is more than welcome. Also feel free to report bugs, request features using github issues.

People are expected to follow the Scala Code Of Conduct when discussing free-arrow on the Github page, Gitter channel, or other venues.

Maintainers

License

MIT License

Written in 2020 by Adriel Casellas.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].