Showing posts with label syntactic sugar. Show all posts
Showing posts with label syntactic sugar. Show all posts

Tuesday, January 26, 2010

Guard Sugar

This topic is a simple tip for a cleaner syntax for guards. Guards/filters are statements in for-comprehensions and case statements that guard or filter matches. Often you will see them as follows:
  1. scala> for (i <- 1 to 10; if (i % 2 == 1)) yield i
  2. res0: scala.collection.immutable.IndexedSeq[Int] = IndexedSeq(1, 3, 5, 7, 9)
  3. scala> util.Random.nextInt(10) match {
  4.      | case i if(i>5) => println("hi")
  5.      | case _ => println("low")
  6.      | }
  7. low

However you have the option to apply a little sugar to the statements cleaning them up a little:
  1. scala> for (i <- 1 to 10; if i % 2 == 1) yield i  
  2. res2: scala.collection.immutable.IndexedSeq[Int] = IndexedSeq(1, 3, 5, 7, 9)
  3. scala> util.Random.nextInt(10) match {          
  4.      | case i if i>5 => println("hi")           
  5.      | case _ => println("low")                 
  6.      | }
  7. hi

As you can see the brackets are optional. That is because in both cases the brackets are not required for the parser to determine the start and end of the guard statements. They are added so that the "normal" syntax used in the typical if statements will compile.
  1. scala> 10 match { case i if i == 1 || i == 10 => "obviously this is a match"
  2. res4: java.lang.String = obviously this is a match
  3. /*
  4. In case statements you can split the if almost any way you want because it is very clearly bound by the if and the => that is required for all case statements with a guard
  5. */
  6. scala> 10 match { case i if i == 1 ||                                        
  7.      | i == 10 => "obviously this is a match"}
  8. res5: java.lang.String = obviously this is a match
  9. scala> 10 match {        
  10.      | case i
  11.      | if
  12.      | i == 1
  13.      | ||
  14.      | i == 10 
  15.      | => "matched"
  16.      | }
  17. res6: java.lang.String = matched
  18. /*
  19. For-comprehensions are not as flexible since it is harder for the compiler to determine where the guard ends. So try to keep it on one line or otherwise make it a function.  That is probably good advice for case statements as well.
  20. */
  21. scala> for { 
  22.      | x <- 1 to 10
  23.      | if
  24.      | x > 10
  25.      | || x == 2 } yield x
  26. < console>:5: error: '<-' expected but integer literal found.
  27.        || x == 2 } yield x
  28.                ^
  29. < console>:5: error: illegal start of simple expression
  30.        || x == 2 } yield x
  31.                  ^
  32. scala> for {              
  33.      | x <- 1 to 10       
  34.      | if x == 1 || x == 10 } yield x
  35. res7: scala.collection.immutable.IndexedSeq[Int] = IndexedSeq(1, 10)

Monday, September 14, 2009

Syntactic sugar

In the spirit of making Scala scalable from scripts up to systems, Scala contains some syntactic sugar to make scripting and internal DSLs a bit easier to write. For example there are several situations where the '.' and '()' for method calls are optional. Semi-colons are optional (except for single-line statements).

There are some corner cases but the basic rule is that you need an odd number of arguments if you wish leave out the '.'.
Examples:
  1. scala> "hello" substring (1,3)
  2. res0: java.lang.String = el
  3. scala> "hello" substring 1
  4. res1: java.lang.String = ello
  5. scala> 1 toString ()
  6. res2: java.lang.String = 1


Precendence runs left to right so:
  1. "hello" contains "hello world" toString ()

becomes
  1. "hello".contains("hello world").toString()

Another example:
  1. scala> "hello" contains "hello world" toString () substring 4
  2. res6: java.lang.String = e
  3. scala> "hello".contains("hello world").toString().substring(4)
  4. res7: java.lang.String = e


There is some operator precendence which we will cover later. Operator precedence allows statements like the following:
  1. scala> "hello" substring 6 - 4
  2. res8: java.lang.String = llo
  3. scala> "hello" substring  (6 - 4)
  4. res9: java.lang.String = llo
  5. scala> "hello".substring  (6 - 4)
  6. res10: java.lang.String = llo