Custom Operator precedence with same identifier


#1

With the code below, I would like to make the following things :

// ... create an group using the "and" keyword
new Lion() and new Dog() and new Cat()
// a group could contain also only one animal
// have a modify method that takes arbitrary number of modifier with a "and" keyword
new Lion() modify SomethingFun("HELLO") and SomethingFun("WORD") 
// understood as ( new Lion() ) modify ( SomethingFun("HELLO") and SomethingFun("WORD") )

Using implicit conversions described in the scala tour https://docs.scala-lang.org/tour/implicit-conversions.html and type inference ( for example , the PetModifiers of type Cat could not apply modification on Lion). Thanks for your help

trait Animal {
  var name: String
  def name(newString : String) { this.name = newString }
}

class Cat extends Animal {
  override var name: String = "Cat"
}

class Dog extends Animal {
  override var name: String = "Dog"
}

class Lion extends Animal {
  override var name: String = "Lion"
}

trait PetModifier[ApplyOn <: Animal] {
  def change(x: ApplyOn): Unit
}

case class SomethingFun(s: String) extends PetModifier[Animal] {
  override def change(x: Animal): Unit = {
    x.name(x + " " + s)
  }
}

class Group[P <: Animal](pets: Stream[P]) extends Iterable[P] {

  // operations : foreach / map / flatmap
  override def iterator = pets.iterator

  def and[B >: P <: Animal](element : B) = {
    new Group(
      Stream.consWrapper(pets).#::(element)
    )
  }
}

class Modifiers[+T](modifiers: => Stream[T]) {
  def and[B <: PetModifier[_]](mod: B) = {
    new Modifiers(
        Stream.consWrapper(modifiers).#::(mod)
    )
  }

  // a iterator to get access to element
  def iterator = modifiers.iterator
}

#2

Operator precedence in Scala is hardcoded: https://docs.scala-lang.org/tour/operators.html

When an expression uses multiple operators, the operators are evaluated based on the priority of the first character

If you want to have precedence rules for you operators then you need to prefix them with appropriate letter, e.g:

def /_and
def +_modify

That would look ugly, though.

Typically, in Scala you use some sort of fluent interface if you really want to avoid parentheses in your DSL (domain specific language). Trying too hard to make smart DSL would probably lead to very complex fluent interfaces and therefore to compilation error messages that are hard to understand by users.

For inspiration about making DSLs I would go to http://www.scalatest.org/ It has plenty of English-like syntax with infix notation, but sometimes requires parentheses.


#3

I know this DSL is hard to understand but I must first implement for the TA it before making nice things :frowning: I wonder how to do it without chaning the sample code ( the first code block )


#4

also asked at https://stackoverflow.com/questions/55663511/scala-custom-operator-precedence-with-same-identifier