views:

172

answers:

3

I've recently given Scala a second chance, and started with the project I always implement (in functional or pseudo-functional languages): an automated reasoner for propositional logic (and later predicate logic).

Now, I've tried to get the notation of propositional logic in the language itself as pretty as possible, and I've gotten this far - with an implicit conversion (String -> Atom):

("A" and "B") implies "C"

The functions "and" and "implies" (and "or" and "equivalent") are simple methods that call the relevant case class constructor. However, when implementing "not", I get stuck with either of the two following notations:

("A" and "B").not
Not("A" and "B")

Is there a way to trick Scala into accepting the desired:

not("A" and "B")

Preferrably without renaming the class "Not" to "not", because I might like to call it "¬" or something else, in th future.

+6  A: 

You can define not as a method on a singleton object, like this:

object Logic {
  def not(x:Expr) = Not(x)
}
import Logic._
not("A" and "B")

(Where Expr is supposed to be the common superclass of And, Or, Not and Atom)

Edit: Here's an example of how this could be used with only a single import:

object Logic {
  abstract class Expr {
    def and(e: Expr) = Conjunction(this, e)
    def or(e: Expr) = Disjunction(this, e)
    def implies(e: Expr) = Implication(this, e)
  }
  case class Conjunction(e1: Expr, e2: Expr) extends Expr
  case class Disjunction(e1: Expr, e2: Expr) extends Expr
  case class Implication(e1: Expr, e2: Expr) extends Expr
  case class Negation(e: Expr) extends Expr
  case class Atom(name: String) extends Expr

  def not(e: Expr) = Negation(e)
  implicit def string2atom(str: String) = Atom(str)
}

// use site
import Logic._
not("A" and "B") implies (not("A") or not("B"))
sepp2k
Thanks, I didn't know I could use "static imports" in Scala - this would leave me with a mandatory import on each page, though, which - together with the implicit conversion would be a a lot of extra code for each use.
Pepijn
@Dennetik: If you simply put everything into the Logic object, `import Logic._` is all you need to use your classes.
sepp2k
@sepp2k: Hadn't thought of that, I still have to get used to Scala's freedom, compared to Java...
Pepijn
+5  A: 

I noticed on this answer to another question that it appears that one can prefix the operator name with unary_ to achive what you are trying to do. (See unary_!.)

Edit: this article confirms the syntax.

Paul Ruane
That doesn't work with custom operator names though. I.e. you can't define `unary_not` to get a prefix "not"-operator.
sepp2k
Specifically `prefix_` only works with `!`, `~`, `+` and `-`.
sepp2k
@sepp2k: Thanks, did not realise this subtlty. This restriction seems like a bit of a shame (and completely arbitrary).
Paul Ruane
@Paul: I don't think it's that arbitrary. I would imagine that not having that restriction would make parsing quite a pain. You wouldn't know whether `foo bar` should parse as `foo.bar()` or `bar.unary_foo()` until after typechecking (and you'd still need rules to decide which one should be chosen if both are possible).
sepp2k
@sepp2k: Ah, very good point.
Paul Ruane
Still, this means I could implement it as the "!" or "~" operator, which is what I'm after.
Pepijn
sepp2k
+5  A: 

Why Not instead of not? There's nothing to stop you from doing this:

object not {
  def apply(expr: T) = ...
}

And then use not("A" and "B").

Daniel