All Projects → Thangiee → Freasy Monad

Thangiee / Freasy Monad

Licence: mit
Easy way to create Free Monad using Scala macros with first-class Intellij support.

Programming Languages

scala
5932 projects
metaprogramming
66 projects
scalaz
18 projects

Projects that are alternatives of or similar to Freasy Monad

Here Be Dragons
An Intellij/Android Studio plugin to help visualise side effects in your code.
Stars: ✭ 325 (+190.18%)
Mutual labels:  intellij, functional-programming
Purescript Spec
Testing framework for Purescript
Stars: ✭ 108 (-3.57%)
Mutual labels:  functional-programming
Sodium Typescript
Typescript/Javascript implementation of Sodium FRP (Functional Reactive Programming) library
Stars: ✭ 102 (-8.93%)
Mutual labels:  functional-programming
Functionaljava
Functional programming in Java
Stars: ✭ 1,472 (+1214.29%)
Mutual labels:  functional-programming
Akka Stream Eventsourcing
Event sourcing for Akka Streams
Stars: ✭ 105 (-6.25%)
Mutual labels:  functional-programming
Fsconfig
FsConfig is a F# library for reading configuration data from environment variables and AppSettings with type safety.
Stars: ✭ 108 (-3.57%)
Mutual labels:  functional-programming
Orkestra
Functional DevOps with Scala and Kubernetes
Stars: ✭ 102 (-8.93%)
Mutual labels:  functional-programming
Lift
constexpr C++17 library for simplifying higher order functions in application code
Stars: ✭ 111 (-0.89%)
Mutual labels:  functional-programming
React Collection Helpers
A suite of composable utility components to manipulate collections.
Stars: ✭ 109 (-2.68%)
Mutual labels:  functional-programming
Church
⛪️ Church Encoding in JS
Stars: ✭ 107 (-4.46%)
Mutual labels:  functional-programming
Pattern Matching Ts
⚡ Pattern Matching in Typescript
Stars: ✭ 107 (-4.46%)
Mutual labels:  functional-programming
Parapet
A purely functional library to build distributed and event-driven systems
Stars: ✭ 106 (-5.36%)
Mutual labels:  functional-programming
Python Memoization
A powerful caching library for Python, with TTL support and multiple algorithm options.
Stars: ✭ 109 (-2.68%)
Mutual labels:  functional-programming
Seleniumwithcucucumber
In this project we will discuss working Selenium with cucumber
Stars: ✭ 104 (-7.14%)
Mutual labels:  intellij
Masala Parser
Javascript Generalized Parser Combinators
Stars: ✭ 110 (-1.79%)
Mutual labels:  functional-programming
Gradle Changelog Plugin
Plugin for parsing and managing the Changelog in a "keep a changelog" style.
Stars: ✭ 102 (-8.93%)
Mutual labels:  intellij
Forge
Functional style JSON parsing in Kotlin
Stars: ✭ 106 (-5.36%)
Mutual labels:  functional-programming
Clear Config
Scala FP configuration library with a focus on runtime clarity
Stars: ✭ 108 (-3.57%)
Mutual labels:  functional-programming
Cat Facts
Daily cat facts! 🐱
Stars: ✭ 110 (-1.79%)
Mutual labels:  cat
Scalajs React
Facebook's React on Scala.JS
Stars: ✭ 1,524 (+1260.71%)
Mutual labels:  functional-programming

Freasy Monad

Freasy Monad library makes it easy to create Free Monad for typelevel/cats and scalaz/scalaz.

Getting started

Important

  • Version 0.6.0 uses scala.meta. If using IntelliJ, please uninstall the Freasy Monad Plugin if you have it installed.

  • Replace addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full) with addCompilerPlugin("org.scalameta" % "paradise" % "3.0.0-M11" cross CrossVersion.full) for those coming from versions before 0.6.0.

Freasy Monad is currently available for Scala 2.11 and 2.12, and Scala.js.

If you are using cats, add the following to your build.sbt:

libraryDependencies ++= Seq(
  "com.github.thangiee" %% "freasy-monad" % "0.7.0",
  "org.typelevel" %% "cats-free" % "1.1.0"
)
addCompilerPlugin("org.scalameta" % "paradise" % "3.0.0-M11" cross CrossVersion.full)

If you are using scalaz, add the following to your build.sbt:

libraryDependencies ++= Seq(
  "com.github.thangiee" %% "freasy-monad" % "0.7.0",
  "org.scalaz" %% "scalaz-core" % "7.2.22"
)
addCompilerPlugin("org.scalameta" % "paradise" % "3.0.0-M11" cross CrossVersion.full)

Starting with version 0.5.0, group ID has been changed from com.thangiee to com.github.thangiee.

@free macro

Key-value store example from cats website using free macro:

  import cats._
  import cats.free._
  import freasymonad.cats.free // or freasymonad.scalaz.free
  import scala.collection.mutable

  @free trait KVStore {                     // you can use any names you like
    type KVStoreF[A] = Free[GrammarADT, A]  // as long as you define a type alias for Free 
    sealed trait GrammarADT[A]              // and a sealed trait.

    // abstract methods are automatically lifted into part of the grammar ADT
    def put[T](key: String, value: T): KVStoreF[Unit]
    def get[T](key: String): KVStoreF[Option[T]]

    def update[T](key: String, f: T => T): KVStoreF[Unit] =
      for {
        vMaybe <- get[T](key)
        _      <- vMaybe.map(v => put[T](key, f(v))).getOrElse(Free.pure(()))
      } yield ()
  }

  object Main extends App {
    import KVStore.ops._
  
    def program: KVStoreF[Option[Int]] =
      for {
        _ <- put("wild-cats", 2)
        _ <- update[Int]("wild-cats", _ + 12)
        _ <- put("tame-cats", 5)
        n <- get[Int]("wild-cats")
      } yield n
  
    val impureInterpreter = new KVStore.Interp[Id] {
      val kvs = mutable.Map.empty[String, Any]
      def get[T](key: String): Id[Option[T]] = {
        println(s"get($key)")
        kvs.get(key).map(_.asInstanceOf[T])
      }
      def put[T](key: String, value: T): Id[Unit] = {
        println(s"put($key, $value)")
        kvs(key) = value
      }
    }
    
    impureInterpreter.run(program)
  }

Above example for scalaz here.

During compile time, KVStore is expanded to something similar to:

  object KVStore {
    import cats._
    import cats.free._
    import scala.language.higherKinds
    sealed trait GrammarADT[A]
    object GrammarADT {
      case class Put[T](key: String, value: T) extends GrammarADT[Unit]
      case class Get[T](key: String) extends GrammarADT[Option[T]]
    }
    object ops {
      type KVStoreF[A] = Free[GrammarADT, A]
      def put[T](key: String, value: T): KVStoreF[Unit] = injectOps.put[GrammarADT, T](key, value)
      def get[T](key: String): KVStoreF[Option[T]] = injectOps.get[GrammarADT, T](key)
      def update[T](key: String, f: T => T): KVStoreF[Unit] = injectOps.update[GrammarADT, T](key, f) 
    }
    object injectOps {
      def put[F[_], T](key: String, value: T)(implicit I: Inject[GrammarADT, F]): Free[F, Unit] = Free.liftF(I.inj(GrammarADT.Put(key, value)));
      def get[F[_], T](key: String)(implicit I: Inject[GrammarADT, F]): Free[F, Option[T]] = Free.liftF(I.inj(GrammarADT.Get(key)));
      def update[F[_], T](key: String, f: T => T)(implicit I: Inject[GrammarADT, F]): Free[F, Unit] =
        for {
          vMaybe <- get[F, T](key)
          _      <- vMaybe.map(v => put[F, T](key, f(v))).getOrElse(Free.pure(()))
        } yield ()
    }
    class Injects[F[_]](implicit I: Inject[GrammarADT, F]) {
      def put[T](key: String, value: T): Free[F, Unit] = injectOps.put[F, T](key, value);
      def get[T](key: String): Free[F, Option[T]] = injectOps.get[F, T](key);
      def update[T](key: String, f: T => T): Free[F, Unit] = injectOps.update[F, T](key, f)
    }
    object Injects {
      implicit def injectOps[F[_]](implicit I: Inject[GrammarADT, F]): Inject[F] = new Inject[F]()
    }
    trait Interp[M[_]] extends ~>[KVStore.GrammarADT, M] {
      def apply[A](fa: KVStore.GrammarADT[A]): M[A] = fa match {
        case GrammarADT.Put(key, value) => put(key, value)
        case GrammarADT.Get(key) => get(key)
      }
      def run[A](op: KVStore.ops.KVStoreF[A])(implicit m: cats.Monad[M]): M[A] = op.foldMap(this)
      def put[T](key: String, value: T): M[Unit]
      def get[T](key: String): M[Option[T]]
    }
  }

Benefits

  • From the expanded version, we can see that the free macro takes care most of the tedious parts when it comes to writing free monad. The macro uses our abstract methods to define the ADT case classes, create smart constructors to case classes, and do pattern matching on the ADT.

  • This library also generate Inject for composing Free monads ADTs. See example for cats and scalaz.

  • Writing an interpreter using Intellij becomes a breeze:

    impl

  • No more false error marks when writing interpreter with Intellij!

    regular-interp freasy-monad-interp

IntelliJ support

Since switch to scala.meta in version 0.6.0, syntax highlighting & code completion in IntelliJ works without needing to install a plugin. Therefore, the Freasy Monad Plugin needs to be uninstalled if you are coming from a previous version.

Constraints

There are some constraints on @free trait, and if violated, will result in a compiler error.

  • Can not define var.
  • All val and def need to have an explicit return type.
  • Abstract val and def must have a return type of the defined type alias. From the example above, this would be KVStoreF[...].
  • private and protected access modifiers are not allowed, use package-private instead.
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].