Edit: I updated the question to be more descriptive.
Note: I use the Scala 2.11 compiler, because that is the compiler version used by the LMS tutorial project.
I am porting a DSL written in Haskell to Scala. The DSL is an imperative language, so I used monads with do-notation, namely WriterT [Stmt] (State Label) a
. I had trouble porting this to Scala, but worked around it by using the ReaderWriterState
monad and just using Unit for the Reader
component. I then went about searching for an alternative for the do-notation found in Haskell. For-comprehensions are supposed to be this alternative in Scala, but they are tailored towards sequences, I e.g. could not pattern match a tuple, it would insert a call to filter
. So I started looking for alternatives and found multiple libraries: effectful
, monadless
, and each
. I tried effectful
first which does exactly what I wanted to achieve, I even prefer it over Haskell's do-notation, and it worked well with the ReaderWriterState
monad from ScalaZ
I had been using. In my DSL I have actions like Drop()
(a case class) that I want to be able to use directly as a statement. I hoped to use implicits for this, but because the !
method of effectful
(or monadless
equivalent for that matter) is too general, I cannot get Scala to automatically convert my Action
case classes to something of type Stmt
(the ReaderWriterState
that returns Unit
).
So if not implicits, would there be a different way to achieve it?
As shown in Main.passes2
, I did figure out a workaround that I would not mind using, but I am curious whether I stumbled upon is some limitation of the language, or is simply my lack of experience with Scala.
With Main.fails1
I will get the following error message: Implicit not found: scalaz.Unapply[scalaz.Monad, question.Label]
. Unable to unapply type question.Label
into atype constructor of kind M[_]
that is classified by the type class scalaz.Monad
. Check that the type class is defined by compiling implicitly[scalaz.Monad[type constructor]]
and review the implicits in object Unapply
, which only cover common type 'shapes.'
Which comes from ScalaZ its Unapply
: https://github.com/scalaz/scalaz/blob/d2aba553e444f951adc847582226d617abae24da/core/src/main/scala/scalaz/Unapply.scala#L50
And with Main.fails2
I will just get: value !
is not a member of question.Label
I assume it is just a matter of writing the missing implicit definition, but I am not quite sure which one Scala wants me to write.
The most important bits of my build.sbt are the version:
scalaVersion := "2.11.2",
And the dependencies:
libraryDependencies += "org.scala-lang" % "scala-compiler" % "2.11.2",
libraryDependencies += "org.scala-lang" % "scala-library" % "2.11.2",
libraryDependencies += "org.scala-lang" % "scala-reflect" % "2.11.2",
libraryDependencies += "org.pelotom" %% "effectful" % "1.0.1",
Here is the relevant code, containing the things I tried and the code necessary to run those things:
package question
import scalaz._
import Scalaz._
import effectful._
object DSL {
type GotoLabel = Int
type Expr[A] = ReaderWriterState[Unit, List[Action], GotoLabel, A]
type Stmt = Expr[Unit]
def runStmt(stmt: Stmt, startLabel: GotoLabel): (Action, GotoLabel) = {
val (actions, _, updatedLabel) = stmt.run(Unit, startLabel)
val action = actions match {
case List(action) => action
case _ => Seq(actions)
}
(action, updatedLabel)
}
def runStmt(stmt: Stmt): Action = runStmt(stmt, 0)._1
def getLabel(): Expr[GotoLabel] =
ReaderWriterState((_, label) => (Nil, label, label))
def setLabel(label: GotoLabel): Stmt =
ReaderWriterState((_, _) => (Nil, Unit, label))
implicit def actionStmt(action: Action): Stmt =
ReaderWriterState((_, label) => (List(action), Unit, label))
}
import DSL._
final case class Label(label: String) extends Action
final case class Goto(label: String) extends Action
final case class Seq(seq: List[Action]) extends Action
sealed trait Action {
def stmt(): Stmt = this
}
object Main {
def freshLabel(): Expr[String] = effectfully {
val label = getLabel.! + 1
setLabel(label).!
s"ants-$label"
}
def passes0() =
freshLabel()
.flatMap(before => Label(before))
.flatMap(_ => freshLabel())
.flatMap(after => Label(after));
def passes1() = effectfully {
unwrap(actionStmt(Label(unwrap(freshLabel()))))
unwrap(actionStmt(Label(unwrap(freshLabel()))))
}
def fails1() = effectfully {
unwrap(Label(unwrap(freshLabel())))
unwrap(Label(unwrap(freshLabel())))
}
def pasess2() = effectfully {
Label(freshLabel.!).stmt.!
Label(freshLabel.!).stmt.!
}
def fails2() = effectfully {
Label(freshLabel.!).!
Label(freshLabel.!).!
}
def main(args: Array[String]): Unit = {
runStmt(passes0())
}
}