Puzzle on LMS with Syntactic Sugaring

Dear all,

I am playing around with Expression Problem in the fashion of LMS. I have faced the following puzzle:

trait NA {
  trait Exp {
    def + (that: Exp) = Add(this, that)
  }
  case class Num(n: Int) extends Exp
  case class Add(l: Exp, r: Exp) extends Exp
}

Now, if I try to add a case for multiplication to my expressions

trait NAM extends NA {  
  trait Exp extends super.Exp {
    def * (that: Exp) = Mul(this, that)
  }
  case class Mul(l: Exp, r: Exp) extends Exp
}

I get the following error

value * is not a member of Num

for the following expression:

Num(2) * Num(5) + Num(4)

Without the syntactic sugaring, everything is fine – as prescribed by LMS. How can I do the syntactic sugaring?

TIA,
–Hossein

I typed it into Repl and it does not work for me either. In fact, when I type it in directly, REPL does not find “Num”. Unless you are omitting something that a newbie would not be aware of?

scala> Num(2)
:12: error: not found: value Num
Num(2)
^

I’m not sure what “LMS” is in this context, but the code you’re showing doesn’t seem to make sense as such. Can you show what you’re trying to do in more detail, maybe as a scalafiddle? In general, I’d usually tackle problems like this using a typeclass, but I don’t even understand what you think it ought to do…

Sorry, I somehow to took it for granted in Scala. LMS = Lightweight Modular Staging. See:

https://scala-lms.github.io/

In the context of Expression Problem (EP), the very oldest research paper is the most related.

Now, the idea in the code is that one likes to add both new cases to NA.Exp and new functions defined on it – without touching NA. NAM adds the Mul case but fails to add the * method. It, of course, is possible to use implicits as follows instead of extending Exp:

trait NAM extends {
  implicit class ExpWithTimes(e: Exp) {
    def * (that: Exp) = Mul(e, that)
  }
  case class Mul(l: Exp, r: Exp) extends Exp
}

But, that’s not really an EP solution for it doesn’t really add the function (i.e., the * method) to the ADT (i.e., NA.Exp).

Interesting. Looks quite useful for certain use cases, but I hadn’t come across it before. Seems like it might be a bit old, though – the front example is using the not-much-loved Manifest, and the “docs” link is broken.

But as I’d expect, the example on the front page is parameterizing over typeclasses. That’s usually the way I would expect to tackle a problem like this – indeed, the Expression Problem is the most common example I usually see for typeclasses. As you’re finding, doing it with inheritance gets problematic quickly…

Well, not every language supports typeclasses. The language support that LMS requires is relatively less advanced. And, that’s expected to make it more accessible to other languages.

I’m wondering whether that’s a real limitation? Or, can it be worked around?

Well, almost anything can be worked around, if you’re willing to do a lot of boilerplate and duplication. But this sort of thing is the reason why typeclasses are increasingly common in idiomatic Scala code: they’re often the cleanest, least-boilerplatey solution to these sorts of problems…

OK. Show me your boilerplate then…

… so we can be the judge of that in this very case. :slight_smile: