Recientemente he descubierto este pequeño scala example llamada intérprete simple usando mónadas:¿De qué sirve usar mónadas en un intérprete?
object simpleInterpreter {
case class M[A](value: A) {
def bind[B](k: A => M[B]): M[B] = k(value)
def map[B](f: A => B): M[B] = bind(x => unitM(f(x)))
def flatMap[B](f: A => M[B]): M[B] = bind(f)
}
def unitM[A](a: A): M[A] = M(a)
def showM(m: M[Value]): String = m.value.toString();
type Name = String
trait Term;
case class Var(x: Name) extends Term
case class Con(n: int) extends Term
case class Add(l: Term, r: Term) extends Term
case class Lam(x: Name, body: Term) extends Term
case class App(fun: Term, arg: Term) extends Term
trait Value
case object Wrong extends Value {
override def toString() = "wrong"
}
case class Num(n: int) extends Value {
override def toString() = n.toString()
}
case class Fun(f: Value => M[Value]) extends Value {
override def toString() = "<function>"
}
type Environment = List[Pair[Name, Value]]
def lookup(x: Name, e: Environment): M[Value] = e match {
case List() => unitM(Wrong)
case Pair(y, b) :: e1 => if (x == y) unitM(b) else lookup(x, e1)
}
def add(a: Value, b: Value): M[Value] = Pair(a, b) match {
case Pair(Num(m), Num(n)) => unitM(Num(m + n))
case _ => unitM(Wrong)
}
def apply(a: Value, b: Value): M[Value] = a match {
case Fun(k) => k(b)
case _ => unitM(Wrong)
}
def interp(t: Term, e: Environment): M[Value] = t match {
case Var(x) => lookup(x, e)
case Con(n) => unitM(Num(n))
case Add(l, r) => for (val a <- interp(l, e);
val b <- interp(r, e);
val c <- add(a, b))
yield c
case Lam(x, t) => unitM(Fun(a => interp(t, Pair(x, a) :: e)))
case App(f, t) => for (val a <- interp(f, e);
val b <- interp(t, e);
val c <- apply(a, b))
yield c
}
def test(t: Term): String =
showM(interp(t, List()))
val term0 = App(Lam("x", Add(Var("x"), Var("x"))), Add(Con(10), Con(11)))
val term1 = App(Con(1), Con(2))
def main(args: Array[String]) {
println(test(term0))
println(test(term1))
}
}
Cuál es el uso/ventaja de cálculos monádicos aquí? De hecho, el M
no es más que una mónada de identidad. ¿Se acaba de presentar para dar un ejemplo de sintaxis monádica o tiene un efecto importante?
Gracias por los enlaces. ¿Pero podría ser más específico y resumir qué tipo de extensibilidad es otorgada por las mónadas? Reemplazar enlace de identidad por ej. una salida de depuración? ¿Qué uso práctico hay para los analizadores? Por cierto: el código anterior no tiene nada que ver con los combinadores de analizador monádico (como se muestra en tu ejemplo de Haskell). – Dario
¿Has leído el documento de Wadler? Él da muchos ejemplos diferentes, siendo el registro uno de ellos.Sé que no estás analizando, pero el ejemplo de Wadler es la interpretación, que es, creo, lo que estás haciendo. –