Description
Freasy Monad library makes it easy to create Free Monad for typelevel/cats
and scalaz/scalaz.
It also integrate nicely with Intellij through a plugin to provide proper highlighting & code completion.
Freasy Monad alternatives and similar packages
Based on the "Extensions" category.
Alternatively, view Freasy Monad alternatives based on common mentions on social networks and blogs.
-
cats
Lightweight, modular, and extensible library for functional programming. -
Cassovary
Cassovary is a simple big graph processing library for the JVM -
scala.meta
Library to read, analyze, transform and generate Scala programs -
Enumeratum
A type-safe, reflection-free, powerful enumeration implementation for Scala with exhaustive pattern match warnings and helpful integrations. -
Chimney
Scala library for boilerplate-free, type-safe data transformations -
Scala-Logging
Convenient and performant logging library for Scala wrapping SLF4J. -
tinylog
tinylog is a lightweight logging framework for Java, Kotlin, Scala, and Android -
Freestyle
A cohesive & pragmatic framework of FP centric Scala libraries -
Scala Graph
Graph for Scala is intended to provide basic graph functionality seamlessly fitting into the Scala Collection Library. Like the well known members of scala.collection, Graph for Scala is an in-memory graph library aiming at editing and traversing graphs, finding cycles etc. in a user-friendly way. -
scribe
The fastest logging library in the world. Built from scratch in Scala and programmatically configurable. -
Each
A macro library that converts native imperative syntax to scalaz's monadic expressions -
Rapture
a collection of libraries for common, everyday programming tasks (I/O, JSON, i18n, etc.) -
Stateless Future
Asynchronous programming in fully featured Scala syntax. -
Scala Blitz
Scala framework for efficient sequential and data-parallel collections - -
Squid
Squid – type-safe metaprogramming and compilation framework for Scala -
Records for Scala
Labeled records for Scala based on structural refinement types and macros. -
Play monadic actions
A simple scala DSL to allow clean and monadic style for Play! Actions -
enableIf.scala
A library that toggles Scala code at compile-time, like #if in C/C++ -
Lamma
Lamma schedule generator for Scala is a professional schedule generation library for periodic schedules like fixed income coupon payment, equity deravitive fixing date generation etc. -
wvlet-log
A library for enhancing your application logs with colors and source code locations. -
Resolvable
A library to optimize fetching immutable data structures from several endpoints in several formats. -
Freedsl
Practical effect composition library based on abstract wrapping type and the free monad
WorkOS - The modern identity platform for B2B SaaS
* Code Quality Rankings and insights are calculated and provided by Lumnify.
They vary from L1 to L5 with "L5" being the highest.
Do you think we are missing an alternative of Freasy Monad or a related project?
README
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)
withaddCompilerPlugin("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:
- No more false error marks when writing interpreter with Intellij!
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
anddef
need to have an explicit return type. - Abstract
val
anddef
must have a return type of the defined type alias. From the example above, this would beKVStoreF[...]
. private
andprotected
access modifiers are not allowed, use package-private instead.