Get your own free workspace
View
 

ScalaFeatures

Page history last edited by PBworks 4 years, 5 months ago

List of Scala Syntax Features:

A list of Scala syntax features that might be useful in our quest.

 

Operators are valid Identifiers

Which also means that all operations are actually method calls. This is made possible since ints, floats etc, are objects instead of primitives. The precedence of a user-defined infix operator is based on the first character of the operator name. It coincides with the operator precedence of Java for those operators that start with an operator character used in the languages. e.g.:

class Complex(real: double, imaginary: double){

    def + (other: Complex) : Complex = {

        //...

    }

}

 

Any single parameter method can be used as an infix operator

Good for syntactic sugar. e.g.:

System exit 0   // call System.exit(int) using infix notation

Thread Sleep 10 // call Thread.sleep(int) using infix notation

 

Singleton Objects

These are classes that only have a single instance and this instance is created on first use. e.g.:

object President extends Person {

    //have methods of the singleton object

}

 

Classes can have parameters

These are called primary constructors. So, no need of separate constructors though it allows secondary constructor declaration in case more than one constructor is needed. e.g.:

class Complex(real: double, imaginary: double){

    //…

}

 

Parameterless methods

Such methods are invoked every time their name is selected. No arguments are passed. e.g.:

class Complex(real: double, imaginary: double){

    def re = real        //re and im are parameter-less methods

    def im = imaginary

}

 

Allows Multiple Inheritance using Traits

[1] A trait is a special form of an abstract class which does not have any value parameters for its constructor. Traits can be used in all contexts where other abstract classes appear; however only traits can be used as mixins (mixins coming up..).

e.g. 

trait AbsIterator[T] {

    def hasNext: boolean

    def next: T

}

 

Next, consider a trait which extends AbsIterator with a method foreach, which applies a given function to every element returned by the iterator.

 

trait RichIterator[T] extends AbsIterator[T] {

    def foreach(f: T => unit): unit =

        while (hasNext) f(next)

}

 

Here is a concrete iterator class, which returns successive characters of a given string:

 

class StringIterator(s: String) extends AbsIterator[char] {

    private var i = 0

    def hasNext = i < s.length

    def next = { val x = s charAt i; i = i + 1; x }

}

 

Mixin-class composition We now would like to combine the functionality of RichIterator and StringIterator in a single class. With single inheritance and interfaces alone this is impossible, as both classes contain member implementations with code. Therefore, Scala provides a mixin-class composition mechanism which allows programmers to reuse the delta of a class definition i.e., all new definitions that are not inherited. This mechanism makes it possible to combine RichIterator with StringIterator, as is done in the following test program. The program prints a column of all the characters of a given string.

 

 

object Test {

    def main(args: Array[String]): unit = {

        class Iter extends StringIterator(args(0)) with RichIterator[char]

        val iter = new Iter

        iter foreach System.out.println

    }

}

The Iter class in function main is constructed from a mixin composition of the parents StringIterator and RichIterator. The first parent is called the superclass of Iter, whereas the second parent is called a mixin.

 

Properties:

For every definition of a variable var x: T in a class, Scala defines setter and getter methods as follows.

 def x: T

 def x_= (newval: T): unit

These methods reference and update a mutable memory cell, which is not accessible directly to Scala programs. Every mention of the name x in an expression is then interpreted as a call to the parameterless method x. Furthermore, every assignment x = e is interpreted as a method invocation x_=(e).

The treatment of variable accesses as method calls makes it possible to define properties (in the C# sense) in Scala. For instance, the following class Celsius defines a property

degree which can be set only to values greater or equal than 273.

 

class Celsius {

  private var d: int = 0

  def degree: int = d

  def degree_=(x: int): unit = if (x >= 273) d = x

}

 

This might allow us to mimic the trigger on replace functionality of JavaFX.

 

Functions 

1. Scala allows anonymous, curried and nested functions. It supports higher order functions. You can define functions, anonymous functions and closures anywhere -- except in top-level scope (compilation unit level).

e.g.:

// define anonymous function and call it

((i:int, j:int) => i+j)(3, 4) // cannot remove int, gives a "missing parameter type" error

 

// nested inner functions can access outer's locals and arguments

def outer(s: String) = {

  def inner() = {

     System.out.println("outer's 's': " + s);

  }

  inner();

}

 

2. Methods can be used as arguments to functions(similar to delegates concept in C#)

e.g. we have a function forall which is true if the predicate passed holds true for all elements of the array (first parameter). we can call this function with row as our array

forall(row, 0 ==))

The expression forall(row, 0 ==) tests whether row consists only of zeros. Here, the == method of the number 0 is passed as argument corresponding to the predicate parameter p. This illustrates that methods can themselves be used as values in Scala.

 

3. Functions are objects with apply methods so function foo(x) is actually foo.apply(x) 

 

4. Special syntax exists for function applications appearing on the left-hand side of an assignment; these are interpreted as applications of an update method.

e.g. a(i) being the array element at index i, the assignment  a(i) = a(i) + 1 is interpreted as a.update(i, a.apply(i) + 1).

Another possibility for trigger replace? if we can extend from Array and overload the update? IF Arrays can be overloaded (they aren't declared as sealed but final .. sealed in Scala means can't be subclassed then what is final in scala?

 

5. Scala allows "for comprehensions"

e.g.

for (val x <xs; 0 <= x) 

Here, val x <xs is a generator, which produces a sequence of values, and 0 <= x is a filter, which eliminates some of the produced values from consideration. 

Compare this to JavaFX for comprehensions to see if it provides all the same functionalities.

 

Case Classes and pattern matching

[3] Scala has "match" expression. Think of it as a generalization of Java's switch statement. While Java's switch statement works only for integers and enums, Scala's pattern matching is more general

e.g.:
def matchTest(x: Any): Any = x match {
// matching integer value "1"
case 1 => "one"
// matches string "two"
// x is a string with "two" as value
case "two" => 2
// x is any int type but with not equal to 1
case y:Int => "scala.Int"
}
Even more complex pattern matchings involve case classes. Case classes are classes with "case" modifier. With "case" classes, you can
  • create objects of case classes without "new" operator - use function call-like syntax
  • case classes have automatic accessor generators for constructor parameter.
  • case classes can be used in pattern matching.
e.g.: 
abstract class Expression;
case class Number(n:int) extends Expression;
case class Add(left:Expression, right:Expression) extends Expression;
case class Var(name: String) extends Expression;
case class Multiply(left:Expression, right:Expression) extends Expression;

// sample match expression that uses above case classes

// This expression simplifies a given expression "e"
// We use x + 0 = x and x * 1 = x rules to simplify

def simplify(e: Expression) : Expression = {
e match {

// matches if expression is a Multiply object
// and right operand is Number 1
case Multiply(x, Number(1)) => x

// matches if expression is a Add object
// and right operand is Number 0
case Add(x, Number(0)) => x

// matches any other expression - no simplification
// just return the original expression
case _ => e
}
}

 

XML Processing

[3] XML literals are supported.

object Main {
def main(args: Array[String]): unit = {
var s = <html>
<title>{args(0)} // the {} syntax to escape to scala is similar to XQuery
</title></html>;

Console.println(s);
}
}

Possible to mix Scala expressions in XML literals - as shown in above example (within {} args[0] is referred). XML elements may be used inside pattern-matching as well (only elements).

[1] The following example shows how to add an entry to a phonebook element.

 

import scala.xml.Node ;

def add(phonebook: Node, newEntry: Node): Node =

    phonebook match {

        case <phonebook>{ cs @ _* }</phonebook> =>            //Question: what does the @ do? Answer: Attribute value.

 

 

             <phonebook>{ cs }{ newEntry }</phonebook>

    }

 

 

val newPhoneBook =

    add(scala.xml.XML.loadFile("savedPhoneBook"),

       <entry>

           <name>Sebastian</name>

           <phone where="work">+41 21 693 68 67</phone>

       </entry>);

 

The add function performs a match on the phonebook element, binding its child sequence to the variable cs (the pattern _* matches an arbitrary sequence). Then it constructs a new phonebook element with child sequence cs followed by the node newEntry.

 

Views

The Scala overview tutorial [1] mentions that "views allow one to augment a class with new members and supported traits." But I am not very clear on the explanation it gives on views :(

the blog [3] mentions the following about views. (slightly more clear)

[3]  "Java closure proposals talk about converting closures to interfaces automatically. I think we can use views in Scala.

// define conversion from any parameterless function to java.lang.Runnable
implicit
def asRunnable(func : ()=>unit) : Runnable = {
new Runnable() {
def run() {
func()
}
}
}

def main ( args : Array[String] ) = {
// create a new thread - passing an anonymous function for Runnable
var t = new Thread(()=>Console.println("hello"));
t.start();

// you can now initialize Runnable with any function
var r : Runnable = ()=>Console.println("I am running!")
r.run();
}

See also: Scala views. "

 

 

References:

[1] An Overview of the Scala Programming Language

[2] A Scala Tutorial for Java Programmers

[3] http://blogs.sun.com/sundararajan/entry/scala_for_java_programmers

 

Comments (0)

You don't have permission to comment on this page.