Skip to content

Commit

Permalink
Feature/0.2.0 (#7)
Browse files Browse the repository at this point in the history
- Remove `Dsl`: no more intermediate type
- Add a converter to turn the companion object into an in-scope interpreter
  • Loading branch information
lloydmeta authored May 17, 2017
1 parent 74bca66 commit 6ae6150
Show file tree
Hide file tree
Showing 21 changed files with 302 additions and 1,256 deletions.
87 changes: 36 additions & 51 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,17 @@ Boilerplate free Tagless Final DSL macro annotation, written in [scala.meta](htt

## General idea

This plugin provides an annotation that cuts out the boilerplate associated with writing composable Tagless
Final DSLs.
This plugin provides an annotation that cuts out the boilerplate associated with writing composable Tagless Final DSLs.

The DSL wrapper methods are generated in the annotated trait's companion object, inside an object called `Ops`
(customisable by passing a name to the annotation as an argument). These are useful when you need to compose
multiple DSLs in the context of `F[_]`.
The Dsl can be accessed directly from the companion object if you import a converter located in `ops`
(customisable by passing a name to the annotation as an argument). This are useful when you need to compose multiple DSLs in the context of `F[_]`, but do not want to name all the interpreter parameters.

Example:

```scala
import cats._, implicits._
import diesel.diesel
object DieselDemo extends App {
import diesel._, cats._, cats.implicits._

object DieselDemo {

// Declare your DSL
@diesel
Expand All @@ -26,27 +24,33 @@ object DieselDemo extends App {
}

@diesel
trait Logging[F[_]] {
trait Logger[F[_]] {
def info(s: String): F[Unit]
}

def addAndLog[F[_]: Monad: Maths: Logging](x: Int, y: Int): F[Int] = {
// Use the auto-generated wrapper methods when composing 2+ DSLs using Monad[F]
import Maths.Ops._, Logging.Ops._
// Import the aliasing converter method
import Maths.ops._, Logger.ops._
def addAndLog[F[_]: Monad: Maths: Logger](x: Int, y: Int): F[Int] = {
for {
r <- add(int(x), int(y))[F]
_ <- info(s"result $r")[F]
r <- Maths.add(Maths.int(x), Maths.int(y))
_ <- Logger.info(s"result $r")
} yield r
}

// Write an interpreter
implicit val interp = new Maths[Id] with Logging[Id] {
def int(a: Int) = a
def add(a: Id[Int], b: Id[Int]) = a + b
def info(msg: String) = println(msg)
}
def main(args: Array[String]): Unit = {

// Wire in our interpreters
implicit val mathsInterp = new Maths[Id] {
def int(a: Int) = a
def add(a: Id[Int], b: Id[Int]) = a + b
}
implicit val loggingInterp = new Logger[Id] {
def info(msg: String) = println(msg)
}

val _ = addAndLog[Id](1, 2)
addAndLog[Id](1, 2)
()
}

}
/*
Expand All @@ -73,32 +77,26 @@ trait Maths[F[_]] {
}
```

is expanded into
is expanded approximately into

```scala
// Your algebra. Implement by providing a concrete F and you have your interpreter
trait Maths[F[_]] {
def int(i: Int): F[Int]

def add(l: F[Int], r: F[Int]): F[Int]
def int(i: Int): F[Int]
def add(l: F[Int], r: F[Int]): F[Int]
}

// Helper methods will be added to the algebra's companion object (one will be created if there isn't one yet)
object Maths {

import diesel.Dsl

// Wrapper methods that allow you to delay deciding on a concrete F, and thus
// are composable with other DSLs
object Ops {
def int(i: Int): Dsl[Maths, Int] = new Dsl[Maths, Int] {
def apply[F[_]](implicit I: Maths[F]): F[Int] = I.int(i)
}

def add(l: Dsl[Maths, Int], r: Dsl[Maths, Int]): Dsl[Maths, Int] = new Dsl[Maths, Int] {
def apply[F[_]](implicit I: Maths[F]): F[Int] = I.add(l.apply[F], r.apply[F])
}
def apply[F[_]](implicit m: Maths[F]): Maths[F] = m

// In charge of aliasing your singleton Maths object to an in-scope Maths[F] :)
object op {
implicit def toDsl[F[_]](o: Maths.type)(implicit m: Maths[F]): Maths[F] = m
}
}

```

## Sbt
Expand All @@ -119,25 +117,12 @@ resolvers += Resolver.url(
// new-style macros. This is similar to how it works for old-style macro
// annotations and a dependency on macro paradise 2.x.
addCompilerPlugin(
"org.scalameta" % "paradise" % "3.0.0-M7" cross CrossVersion.full)
"org.scalameta" % "paradise" % "3.0.0-M8" cross CrossVersion.full)

scalacOptions += "-Xplugin-require:macroparadise"

```

There are also 2 sub-projects that provide implicit conversions from Dsl to Monad so that you can compose multiple
DSLs *without* having to write `[F]` everywhere.

For an example of how this looks, take a look [here](https://github.com/lloydmeta/diesel/blob/master/examples/src/main/scala/KVSLoggingApp.scala#L43-L55)

```scala
// Choose one or the other:

// for cats
libraryDependencies += "com.beachape" %% "diesel-cats" % s"$latest_version"
// for scalaz
libraryDependencies += "com.beachape" %% "diesel-scalaz" % s"$latest_version"
```

# Credit

Expand Down
37 changes: 5 additions & 32 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
lazy val theVersion = "0.1.13-SNAPSHOT"
lazy val theVersion = "0.2.0-SNAPSHOT"

lazy val theScalaVersion = "2.11.11"
lazy val scalaVersions = Seq("2.11.11", "2.12.2")
Expand All @@ -19,7 +19,7 @@ lazy val root = Project(id = "diesel-root", base = file("."))
publishArtifact := false,
publishLocal := {}
)
.aggregate(coreJs, coreJvm, catsJs, catsJvm, scalazJs, scalazJvm, examplesJs, examplesJvm)
.aggregate(coreJs, coreJvm, examplesJs, examplesJvm)

lazy val core = crossProject
.crossType(CrossType.Pure)
Expand All @@ -40,34 +40,6 @@ lazy val core = crossProject
lazy val coreJs = core.js
lazy val coreJvm = core.jvm

lazy val cats = crossProject
.crossType(CrossType.Pure)
.settings(
name := "diesel-cats",
commonSettings,
metaMacroSettings,
publishSettings,
testSettings,
libraryDependencies += "org.typelevel" %%% "cats-core" % catsVersion
)
.dependsOn(core)
lazy val catsJs = cats.js
lazy val catsJvm = cats.jvm

lazy val scalaz = crossProject
.crossType(CrossType.Pure)
.settings(
name := "diesel-scalaz",
commonSettings,
metaMacroSettings,
publishSettings,
testSettings,
libraryDependencies += "org.scalaz" %%% "scalaz-core" % "7.2.12"
)
.dependsOn(core)
lazy val scalazJs = scalaz.js
lazy val scalazJvm = scalaz.jvm

lazy val examples = crossProject
.crossType(CrossType.Pure)
.settings(
Expand All @@ -77,9 +49,10 @@ lazy val examples = crossProject
metaMacroSettings,
publishSettings,
publishArtifact := false,
publishLocal := {}
publishLocal := {},
libraryDependencies += "org.typelevel" %%% "cats-core" % catsVersion
)
.dependsOn(cats)
.dependsOn(core)
lazy val examplesJs = examples.js
lazy val examplesJvm = examples.jvm

Expand Down
88 changes: 0 additions & 88 deletions cats/src/main/scala/diesel/implicits/monadic.scala

This file was deleted.

97 changes: 0 additions & 97 deletions cats/src/main/scala/diesel/implicits/monadicplus.scala

This file was deleted.

Loading

0 comments on commit 6ae6150

Please sign in to comment.