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.
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
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)
Related
In order to use infix notation, I have the following example of scala code.
implicit class myclass(n:Int ){
private def mapCombineReduce(map : Int => Double, combine: (Double,Double) => Double, zero: Double )(a:Int, b:Double): Double =
if( a > b) zero else combine ( map(a), mapCombineReduce(map,combine,zero)(a+1,b) )
var default_value_of_z : Int = 0
def sum( z : Int = default_value_of_z) = mapReduce( x=>x , (x,y) => x+y+z, 0)(1,n)
def ! = mapCombineReduce( x=> x, (x,y) => x*y, 1)(1,n)
}
4 !
4 sum 1 //sum the elements from 1 to 7 and each time combine the result, add 1 to the result.
4 sum
Is there any way in scala 2.12 to run 4 sum without have a double declaration of the sum method inside myclass ?
No, because default arguments are only used if argument list is provided
def f(x: Int = 1) = x
f // interpreted as trying to do eta-expansion
In fact starting Scala 3 it will indeed eta-expand
scala> def f(x: Int = 1) = x
def f(x: Int): Int
scala> f
val res1: Int => Int = Lambda$7473/1229666909#61a1990e
so in your case you will have to write 4.sum() with argument list present.
Consider 2+7 == 3+6 (mod 5). Can you some how use scala syntactic sugar to achieve the same in scala code?
Keep in mind that 2+7 and 3+6 are regular scala Int so overriding the + or == to be mod 5 doesn't work. I'm actually interested in more complex congruences on algebras A. I could do A.congruent(a,b), and write that with some nice symbols like A.~(a,b), but I am interested in a == b (A) or a ==(A) b or perhaps A(a == b). Something where the congruence appears inbetween the terms a and b.
The bottom line of my struggles is that the congruence is defined for type A, and a,b are some elements passed to A but not actually of type A. E.g. A might be a group of matrices and the congruence is if individual matrices a and b differ by a scalar i.e. a*b^-1=sI_n. In particular, a, b will live inside of many groups and the congruence will change based on that. So I it isn't possible to simply add a reference within a and b back to A.
Some how the right solution seems to be the mathematical one, label the equivalence with A not the variables a and b. Yet scala syntactic sugar may not have such a sweetness in mind. Any suggestions appreciated.
Try this:
implicit class ModEquals(a: Int) {
def %%(n: Int) = new { def ===(b: Int) = (a - b) % n == 0 }
}
Usage:
7 %% 3 === 10
This solution enriches Ints with a %% method that takes the congruence. In this example, it's just modulu, but this can easily be extended to anything. The returned object is a class that has an === method defined to implements the equality check.
import scala.languageFeature.implicitConversions
import scala.languageFeature.reflectiveCalls
case class ModuloArg(list: List[Int]) {
assert(list.size > 1)
def ==%%?(m: Int) = {
val hm = list.head % m
list.tail.filter(i => (i % m) != hm).isEmpty
}
def ::%%(n: Int) = ModuloArg(n :: list)
}
implicit class ModuloArgOps(i: Int) {
def ::%%(n: Int) = ModuloArg(n :: i :: Nil)
}
Now, you can use these to check modulo equality,
// 4 == 10 (mod 3)
scala> val mod3Equal4And10 = 4 ::%% 10 ==%%? 3
// mod3Equal4And10: Boolean = true
// 4 == 11 (mod 3)
scala> val mod3Equal4And11 = 4 ::%% 11 ==%%? 3
//mod3Equal4And11: Boolean = false
// 4 == 10 == 13 == 16 (mod 3)
scala> val mod3Equal4And10And13And16 = 4 ::%% 10 ::%% 13 ::%% 16 ==%%? 3
// mod3Equal4And10And13And16: Boolean = true
I'm following along the Coursera course on functional programming in Scala and came along a weird behavior of the worksheet repl.
In the course a worksheet with the following code should give the following results on the right:
object rationals {
val x = new Rational(1, 2) > x : Rational = Rational#<hash_code>
x.numer > res0: Int = 1
y. denom > res1: Int = 2
}
class Rational(x: Int, y: Int) {
def numer = x
def denom = y
}
What I get is
object rationals { > defined module rationals
val x = new Rational(1, 2)
x.numer
y. denom
}
class Rational(x: Int, y: Int) { > defined class Rational
def numer = x
def denom = y
}
Only after moving the class into the object I got the same result as in the code.
Is this caused by Intellij, or have there been changes in Scala?
Are there other ways around this?
In the IntelliJ IDEA scala worksheet handles values inside the objects differently than Eclipse/Scala IDE.
Values inside objects are not evaluated in linear sequence mode, instead they are treated as normal scala object. You barely see information about it until explicit use.
To actually see your vals and expressions simply define or evaluate them outside any object\class
This behaviour could be a saviour in some cases. Suppose you have that definitions.
val primes = 2l #:: Stream.from(3, 2).map(_.toLong).filter(isPrime)
val isPrime: Long => Boolean =
n => primes.takeWhile(p => p * p <= n).forall(n % _ != 0)
Note that isPrime could be a simple def, but we choose to define it as val for some reason.
Such code is nice and working in any normal scala code, but will fail in the worksheet, because vals definitions are cross-referencing.
But it you wrap such lines inside some object like
object Primes {
val primes = 2l #:: Stream.from(3, 2).map(_.toLong).filter(isPrime)
val isPrime: Long => Boolean =
n => primes.takeWhile(p => p * p <= n).forall(n % _ != 0)
}
It will be evaluated with no problem
In the following statement the val f is defined as a lambda that references itself (it is recursive):
val f: Int => Int = (a: Int) =>
if (a > 10) 3 else f(a + 1) + 1 // just some simple function
I've tried it in the REPL, and it compiles and executes correctly.
According to the specification, this seems like an instance of illegal forward referencing:
In a statement sequence s[1]...s[n] making up a block, if a simple
name in s[i] refers to an entity defined by s[j] where j >= i,
then for all s[k] between and including s[i] and s[j],
s[k] cannot be a variable definition.
If s[k] is a value definition, it must be lazy.
The assignment is a single statement, so it satisfied the j >= i criteria, and it is included in the interval of statements the two rules apply to (between and including s[i] and s[j]).
However, it seems that it violates the second rule, because f is not lazy.
How is that a legal statement (tried it in Scala 2.9.2)?
You probably tried to use this in the REPL, which wraps all contents in an object definition. This is important because in Scala (or better: on the JVM) all instance values are initialized with a default value, which is null for all AnyRefs and 0, 0.0 or false for AnyVals. For method values this default initialization does not happen, therefore you get an error message in this case:
scala> object x { val f: Int => Int = a => if (a > 10) 3 else f(a+1)+1 }
defined object x
scala> def x { val f: Int => Int = a => if (a > 10) 3 else f(a+1)+1 }
<console>:7: error: forward reference extends over definition of value f
def x { val f: Int => Int = a => if (a > 10) 3 else f(a+1)+1 }
^
This behavior can even lead to weird situations, therefore one should be careful with recursive instance values:
scala> val m: Int = m+1
m: Int = 1
scala> val s: String = s+" x"
s: String = null x
I would like to write conditional statements mixing transparently Scala Options and regular variables.
For example:
var o1 = Some(1)
var o2: Option[Int] = None
var x = 2
val test1 = x < 3 && o1<5 //=> should be true or Some(true)
val test2 = x < 3 && o2<5 //=> should be false or None
val test3 = x < 3 || o2<5 //=> should be true (o2 not evaluated)
of course I could write
test1 = x < 3 && o1.exists (_<5)
but I would prefer a cleaner syntax.
Any hint? Should I expand 'Option' with operators, or use implicits, or category theory or else?
What about mapping from Option[Int] to Option[Boolean]?
x < 3 && (o1 map {_ < 5} getOrElse false)
x < 3 && (o2 map {_ < 5} getOrElse false)
x < 3 || (o2 map {_ < 5} getOrElse false)
Using implicits is certainly easy:
implicit def enrichOptionInt(self: Option[Int]) = new {
def <(i: Int) = self.exists(_ < i)
}
val test1 = x < 3 && o1 < 5 // True
Or if you want it to work for any kind of Numeric:
class EnrichedOptionNumeric[N: Numeric](self: Option[N]) {
def <(n: N) = self.exists(v => implicitly[Numeric[N]].lt(v, n))
}
implicit def enrichOptionNumeric[N: Numeric](self: Option[N]) = new EnrichedOptionNumeric(self)
val oD = Some(2.0)
val test1 = x < 3 && o1 < 5 // true
val testD = x < 3 && oD < 5.0 // true
EDIT to answer question in comment:
If you want to support equality, you, unfortunately, cannot use == since that operator is already defined for Option. If a method (or operator) is already defined for a class, then the implicit will never be triggered since Scala only looks for implicits when it doesn't recognize the method being called.
You can however, simply define a different symbol to mean "option equals". For example, you could use ===. To do so, you'd just add the following line to the definition of EnrichedOptionNumeric above:
def ===(n: N) = self.exists(v => implicitly[Numeric[N]].equiv(v, n))
Then you can do:
val testE = x < 3 && o1 === 1 // true
Think about whether there's a more natural way to express the "missing" value in the context of this problem than using an Option. Maybe to say there's no limit, a Double with a value of Infinity would work, or an Int with Integer.MAX_VALUE. Then remove the Option from the problem completely. If you can't then you can use
var o1 = Some (1)
var o2 = Option[Int] = None
var x = 2
val test1 = x < 3 && o1.getOrElse(Integer.MAX_VALUE)<5 //=> should be true or Some(true)
val test2 = x < 3 && o2.getOrElse(Integer.MAX_VALUE)<5 //=> should be false or None
val test3 = x < 3 || o2.getOrElse(Integer.MAX_VALUE)<5 //=> should be true (o2 not evaluated)
Tomasz Nurkiewicz's answer can be made more succinct with some Scalaz sugar, and by employing the Monoid instance for Boolean.
The "zero" value for the monoid is false, so o getOrElse false becomes ~o, using a unary operator ~ defined in scalaz. (Here I am using scalaz 6.0.4)
def p: Int => Boolean = _ < 3
def q: Int => Boolean = _ < 5
import scalaz._, Scalaz._
scala> val test1 = p(x) && ~(o1 map q)
test1: Boolean = true
scala> val test2 = p(x) && ~(o2 map q)
test2: Boolean = false
scala> val test3 = p(x) || ~(o2 map q)
test3: Boolean = true
Scala can actually make an Ordering for Option[A] if you have an Ordering[A] but the semantics are different (None is the smallest value) from what you wanted.
Also, for the comparison to work, both values need to be of the same type, so you need a way to lift the Ints into an Option. I added an opt method for that purpose.
This is an example with the internal ordering:
import scala.math.Ordering.Implicits.infixOrderingOps
//This allows you to use method/operator syntax on anything with an Ordering
implicit def mkOption[A](a: A) = new { def opt: Option[A] = Some(a) }
var o1 = Some(1)
var o2: Option[Int] = None
var x = 2
val test1 = x < 3 && o1<5.opt //=> true
val test2 = x < 3 && o2<5.opt //=> true
val test3 = x < 3 || o2<5.opt //=> true
None > 0.opt //=> false
None < 0.opt //=> true
To get closer to your semantics, we can define a new ordering
implicit def mkOptionOrdering[A: Ordering] = new Ordering[Option[A]] {
def compare(a: Option[A], b: Option[A]): Int = {
if (a.isEmpty || b.isEmpty) 0
else implicitly[Ordering[A]].compare(a.get, b.get)
}
}
Now your tests will do what you expected and the 2 extra tests will also be false but those semantics are rather odd, compare returns 0 for things that aren't equal.