Defining an object as an operation on two other objects in Scala

I am performing a sign analysis at Scala for a laboratory at a university. For this I need arithmetic for abstract values ​​such as Pos , Neg , Zero , NonPos , NonNeg , ... Therefore, I need to declare methods + , - , * , / etc ... these abstract values. I don’t need to define everything at all, but it can determine the "core" of operations on Pos , Neg and Zero , and then use the upper bounds to determine, for example:

 NonPos + Pos = leastUpperBound(Zero + Pos, Neg + Pos) 

where, for example, leastUpperBound(Zero, Neg) = NonPos

In Scala, I use case objects to represent values ​​and leastUpperBound() on each of them. But I still have the code I can’t get rid of, for example, I define:

 case object NonNeg extends Sign { def +(other: Sign) = leastUpperBound(Zero + other, Pos + other) def -(other: Sign) = ... def * = ... ... } 

and I have to do the same for:

 case object NonPos extends Sign { def +(other: Sign) = leastUpperBound(Zero + other, Neg + other) ... } 

and again:

 case object NonZero extends Sign { def +(other: Sign) = leastUpperBound(Neg + other, Neg + other) ... } 

I wonder if it is possible to have some kind of “factory type” so that I can say something in the spirit:

 case object NonNeg extends UpperBoundSign[Pos, Zero] 

My intuition is that with Pos and Zero object s will not be possible, but I am not familiar with Scala, so I can forget some function or template that let me do this.

Does anyone have any ideas to remove this duplication? Maybe Scala macros in 2.10 are well suited for this problem?

I hope the question is clear, thanks.

EDIT: thanks to @cmbaxter's answer and some refactoring on my part, I came up with a solution that I like. If anyone is interested in this, you can find him there: https://gist.github.com/Ricordel/5553405 .

+4
source share
3 answers

I think you can be confusing type identifiers and class instances. I believe that in order to get the necessary functionality, you need to define UpperBoundSign as an abstract class with two constructor arguments, and not with a typical type with two type identifier slots. Here is a simplified solution that might work for what you want to do. I apologize if this is not at all what you wanted:

 trait Sign{ def +(other: Sign):Sign } abstract class UpperBoundSign(pos:Sign, neg:Sign) extends Sign{ def leastUpperBound(pos:Sign, neg:Sign):Sign def +(other: Sign) = leastUpperBound(pos + other, neg + other) } case object Pos extends Sign{ def +(other:Sign) = ... } case object Neg extends Sign{ def +(other:Sign) = ... } case object NonNeg extends UpperBoundSign(Pos, Neg){ def leastUpperBound(pos:Sign, neg:Sign) = ... } 
+5
source

OK, I'm sorry, I misunderstood your question. Anyway, I tried your code, and it looks pretty interesting, but since I like simplicity, I tried to come up with a simpler solution to your problem.

 object Sign { case object Pos extends Sign case object Neg extends Sign case object Zero extends Sign case object Undefined extends Sign case object NonPos extends SignSet(Set(Neg, Zero)) { override def toString = "NonPos" } case object NonNeg extends SignSet(Set(Pos, Zero)) { override def toString = "NonNeg" } case object NonZero extends SignSet(Set(Pos, Neg)) { override def toString = "NonZero" } case object AnySign extends SignSet(Set(Pos, Neg, Zero)) { override def toString = "AnySign" } private val signs = List(Pos, Neg, Zero, Undefined, NonPos, NonNeg, NonZero, AnySign) private def calc(op: Symbol, s1: Sign, s2: Sign): Sign = { val sign = _calc(op, s1, s2) signs.find(_ == sign).getOrElse(sign) } private def _calc(op: Symbol, s1: Sign, s2: Sign): Sign = (op, s1, s2) match { case (op, set: SignSet, sign) => set.flatMap(s => _calc(op, s, sign)) case (op, sign, set: SignSet) => set.flatMap(s => _calc(op, sign, s)) case (_, Undefined, _) => Undefined case (_, _, Undefined) => Undefined case ('+, x, y) if x == y => x case ('+, x, Zero) => x case ('+, Zero, x) => x case ('+, Pos, Neg) => SignSet(Pos, Neg, Zero) case ('+, Neg, Pos) => SignSet(Pos, Neg, Zero) case ('-, x, Neg) => _calc('+, x, Pos) case ('-, x, Pos) => _calc('+, x, Neg) case ('-, x, Zero) => x case ('*, Zero, _) => Zero case ('*, Pos, x) => x case ('*, Neg, Pos) => Neg case ('*, Neg, Neg) => Pos case ('*, Neg, Zero) => Zero case ('/, _, Zero) => Undefined case ('/, x, y) => _calc('*, x, y) } } sealed trait Sign { import Sign.calc def +(other: Sign) = calc('+, this, other) def -(other: Sign) = calc('-, this, other) def *(other: Sign) = calc('*, this, other) def /(other: Sign) = calc('/, this, other) def flatten: Sign = this def |(other: Sign): Sign = other match { case sign if sign == this => this case SignSet(signs) => SignSet(signs + this) case sign => SignSet(this, sign) } } object SignSet { def apply(signs: Set[Sign]) = new SignSet(signs) def apply(signs: Sign*) = new SignSet(signs.toSet) def unapply(set: SignSet) = Some(set.signs) } class SignSet(val signs: Set[Sign]) extends Sign { def flatMap(f: Sign => Sign) = SignSet(signs.map(f)).flatten override def flatten = signs.map(_.flatten).reduce(_ | _) override def |(other: Sign) = other match { case SignSet(otherSigns) => SignSet(otherSigns | signs) case sign => SignSet(signs + sign) } override def toString = signs.mkString("SignSet(", ", ", ")") def equals(other: SignSet) = signs == other.signs override def equals(other: Any) = other match { case set: SignSet => equals(set) case _ => false } } import Sign._ println(Pos / NonPos) println(Pos + Neg) println(NonZero * Zero) println(NonZero / NonPos) println(NonZero - NonZero) println(NonZero + Zero) 
+1
source

You can try it with set theory.

 abstract class Sign(name: String) { def contains(sign: Sign) = sign eq this def +(other: Sign) = findSign(Union(this, other)) def -(other: Sign) = findSign(Difference(this, other)) def equals(other: Sign) = (contains(Pos), contains(Neg), contains(Zero)) == (other.contains(Pos), other.contains(Neg), other.contains(Zero)) override def equals(other: Any) = { if(other.isInstanceOf[Sign]) equals(other.asInstanceOf[Sign]) else false } override def toString = name } case class Union(sign1: Sign, sign2: Sign, name: String = "Union") extends Sign(name) { override def contains(sign: Sign) = sign1.contains(sign) || sign2.contains(sign) } case class Intersection(sign1: Sign, sign2: Sign, name: String = "Intersection") extends Sign(name) { override def contains(sign: Sign) = (sign1.contains(sign) || sign2.contains(sign)) && !(sign1.contains(sign) && sign2.contains(sign)) } case class Difference(sign1: Sign, sign2: Sign, name: String = "Difference") extends Sign(name) { override def contains(sign: Sign) = sign1.contains(sign) && !sign2.contains(sign) } case class Negation(sign: Sign, name: String = "Negation") extends Sign(name) { override def contains(s: Sign) = !sign.contains(s) } case object Zero extends Sign("Zero") case object Pos extends Sign("Pos") case object Neg extends Sign("Neg") val NonPos = Negation(Pos, "NonPos") val NonNeg = Negation(Neg, "NonNeg") val NonZero = Negation(Zero, "NonZero") val AnySign = Union(NonZero, Zero, "AnySign") val NoSign = Negation(AnySign, "NoSign") val signs = List(Zero, Pos, Neg, NonPos, NonNeg, NonZero, AnySign, NoSign) def findSign(sign: Sign) = signs.find(_ == sign).get println(Pos + Neg) println(NonNeg - Zero) println(NonZero + Zero) println(Pos + Neg + Zero) println(NonPos - Neg - Zero) 
0
source

Source: https://habr.com/ru/post/1479887/


All Articles