Skip to content

lampepfl/monadic-reflection

main
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Code

Latest commit

 

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
 
 
 
 
 
 
 
 
 
 

Monadic Reflection

This project provides support for monadic reflection (Filinski 1994, 1999) to integrate monadic code with direct style code.

Tired of writing code using for-comprehensions?

The monadic-reflection library provides a convenient alternative!

Before

for {
  _ <- monadicActionA()
  r <- monadicActionB()
  result <- if (predicate(r)) {
    monadicActionC()
  } else {
    monadicActionD()
  }
} yield result

After

effectfulActionA()
if (predicate(effectfulActionB())) {
  effectfulActionC()
} else {
  effectfulActionD()
}

Looks familiar? Yes, it is just the direct-style code you would write in an imperative programming language.

Concepts

The underlying idea is very simple: Instead of using your monadic type constructor M[A] everywhere, your effectful programs now have the type CanReflect[M] ?=> A where CanReflect is a type defined by the monadic-reflection library.

As you can see from the type, given the capability CanReflect[M] you immediately get a value of type A that you can just use in direct-style. No need for flatMap and friends.

The best thing is, that you can go back and forth between the two representations:

trait Monadic[M[_]] {
  // embed a monadic value into direct style
  def reflect[R](mr: M[R])(using r: CanReflect[M]): R = r.reflect(mr)

  // reveal the monadic structure of a direct-style program
  def reify[R](prog: CanReflect[M] ?=> R): M[R]
}

How can I use this with my monad?

All you need to do is implement the Monadic trait which has two abstract methods:

def pure[A](a: A): M[A]
def sequence[X, R](init: M[X])(f: X => Either[M[X], M[R]]): M[R]

The first should look very familiar to you -- and if you already have a monad is very easy to implement. The second is just a slight variation of flatMap. In order to be stack safe you need to make sure to either implement sequence as a tail recursive function, or perform trampolining on your own.

Well, there is a fineprint: You also need to run your programs using a special JDK fork called "Project Loom". See below for more details.

Example Integrations

We provide a few case studies showing how to program with established monadic libraries in direct style:

Dependencies

To implement monadic reflection we require some implementation of (delimited) continuations. At the moment, our library only runs on a open JDK fork called project loom with runtime support for coroutines / delimited continuations.

Download a Loom-enabled JDK

There are early-access builds available at https://jdk.java.net/loom/.

Build Loom

To build the custom JVM yourself, clone the repository

git clone https://github.com/openjdk/loom

and checkout the continuation branch cont:

git checkout fibers

Detailed instructions on how to build the JDK can be found in the file doc/building.md, in short those are:

bash configure
make images

Run Sbt

Finally, run sbt with the newly built JVM. Assuming you checked out loom into PATH and built on a mac, run:

sbt -java-home $PATH/build/macosx-x86_64-server-release/images/jdk

Obviously the path needs to be adjusted for other operating systems.

Some experimental performance optimizations of project loom can be enabled by

-XX:-DetectLocksInCompiledFrames -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseNewCode

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages