2

Scala does not provide chained comparisons as Python does:

// Python:
0 < x <= 3
// Scala:
0 < x && x <= 3

Will Scala 2.10 with the new macro feature enable the programmer write a library that adds this feature? Or is this beyond the scope of Scala's macros?

Macros seem to be the right choice for the implementation of such syntactic sugar as they do not complicate the parser/compiler.

om-nom-nom
  • 62,329
  • 13
  • 183
  • 228
Bastian
  • 4,638
  • 6
  • 36
  • 55

2 Answers2

6

You don't need macros for it:

class ChainedComparisons[T : Ordering](val res: Boolean, right: T) {
  def <^ (next: T) = new ChainedComparisons(res && Ordering[T].lt(right, next), next)
  def <=^ (next: T) = new ChainedComparisons(res && Ordering[T].lteq(right, next), next)
}
implicit def chainedComparisonsToBoolean(c: ChainedComparisons[_]) = c.res

class StartChainedComparisons[T : Ordering](left: T) {
  def <^(right: T) = new ChainedComparisons(Ordering[T].lt(left, right), right)
  def <=^(right: T) = new ChainedComparisons(Ordering[T].lteq(left, right), right)
}
implicit def toStartChainedComparisons[T : Ordering](left: T) = new StartChainedComparisons(left)

Usage:

scala> val x = 2
x: Int = 2

scala> 1 <^ x : Boolean
res0: Boolean = true

scala> 1 <^ x <^ 3 : Boolean
res1: Boolean = true

scala> 1 <^ x <^ 2 : Boolean
res2: Boolean = false

scala> 1 <^ x <=^ 2 : Boolean
res3: Boolean = true

scala> if (1 <^ x <^ 3) println("true") else println(false)
true

scala> 1 <=^ 1 <^ 2 <=^ 5 <^ 10 : Boolean
res5: Boolean = true
senia
  • 37,745
  • 4
  • 88
  • 129
  • 1
    Well that seems to be a feasible solution, two remarks though: 1. Having to annotate the type for each expression is somehow inconvenient. 2. Would it not be quite slow at runtime? Could the latter issue be solved by using macros to convert the ChainComparison tree to standard comparisons? – Bastian Nov 19 '12 at 09:44
  • 1. You don't always have to annotate type: see `if` example. – senia Nov 19 '12 at 09:52
  • 2. It depends on JIT, but it could be slow. – senia Nov 19 '12 at 09:54
  • -> 2. So macros for the rescue (by extending your approach)? I would definitely like to see that implemented :-). – Bastian Nov 19 '12 at 10:03
  • 2. I don't think it's posible to improve `ChainedComparisons` with macros. You have to create new object on each step since you have to store 2 values: `res` and `right`. – senia Nov 19 '12 at 10:30
  • 2. You can use `value class` on `StartChainedComparisons`, so that no instancies of `StartChainedComparisons` will be created. – senia Nov 19 '12 at 10:34
  • 2. And you can make `ChainedComparisons` mutable to use the same instance of it on each step, but it's an ugly way. – senia Nov 19 '12 at 10:37
2

I don't think Scala macros will help here... (and please correct me if I'am wrong, Eugene will certainly check this)

Macros can only be applied on a type-checked AST (and produce also a type-checked AST). Here the problem is that the expression:

0 < x <= 3

Will be evaluate to: (see another post)

((0 < x) <= 3) // type error

and there no such function <=(i: Int) in Boolean.

I don't see a way to make this expression compiling, thus macros are helpless.

Of course you could use a custom class to achieve your goal, but without macros (I could give you an example if needed), a possible syntax could be 0 less x lesseq 3 or x between (0, 3)

Community
  • 1
  • 1
Lomig Mégard
  • 1,828
  • 14
  • 18
  • @senia just give an example of a solution without macros. – Lomig Mégard Nov 19 '12 at 09:37
  • Well, you could add your own implicit function: `implicit def int2Boolean(i: Int): Boolean = true` So that the compiler has a way to type-check the expression to the following: `(0 < x) <= int2Boolean(3)` Maybe if now you use int2Boolean as a macro, you can modify the whole tree around? – Mikaël Mayer Jun 20 '13 at 09:49