In pursuit of expressivity:
Groovy and Scala compared



          Chris Richardson
     Author of POJOs in Action
   Founder of CloudFoundry.com
     chris@chrisrichardson.net
           @crichardson
Overall presentation goal


 Examine the good,
 the bad and the ugly
      aspects of
    developing with
   Groovy and Scala

     4/3/11   Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                          Slide 2
About Chris
              •    Grew up in England and live in Oakland, CA
              •    Over 25+ years of software development
                   experience including 14 years of Java
              •    Speaker at JavaOne, SpringOne, NFJS,
                   JavaPolis, Spring Experience, etc.
              •    Organize the Oakland JUG (
                   http://bit.ly/ebjava) and the Groovy Grails
                   meetup (http://bit.ly/oakgrails)




                                     http://www.theregister.co.uk/2009/08/19/springsource_cloud_foundry/



     4/3/11        Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                                           Slide 3
Agenda
o  Evolution of programming
   languages
o  Favorite Groovy features
o  The frustration of using Groovy
o  Scala: expressiveness and compile-
   time checking
o  The trouble with Scala



      4/3/11   Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                           Slide 4
Lots of programming languages
 This site lists 8512 languages, complete with 17837 bibliographic records
 featuring 11064 extracts from those references. It is in effect a family
 tree of languages with 5445 links, making it not only the biggest
 programming language family tree around, but also one of the largest
 idea-genealogical projects undertaken.

 http://hopl.murdoch.edu.au/




               4/3/11             Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                              Slide 5
The Elite v. The Masses

The Elite                                             Mainstream

o  Lisp                                               o  Thought Lisp and
   n    Invented in the 1960s                           Smalltalk were:
   n    Dynamic functional                                     n        Weird (Too different
         language                                                         from Algol)
   n    Dynamic object-oriented                                n        Too resource hungry
         language (mid-1980s)                                             (probably were for the
o  Smalltalk                                                              time)
   n    Invented in the 1970s                        o  So the mainstream used:
   n    Dynamic object-oriented
                                                         Pascal, C, C++, ….
         language                                     o  Not so safe, manual
o  Safe, garbage collection,                             memory management,
   rich, …                                               primitive, …




            4/3/11    Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                             Slide 6
LISP/CLOS – an early (late 1980s)
dynamic language
 (defclass Account ()
   ((account-id :accessor account-id :initarg :account-id)
    (balance :accessor account-balance :initarg :balance))                                    Develop by adding and
 )                                                                                            changing program
                                                                                              elements in a running VM
 (defmethod debit ((Account account) amount)
  (decf (account-balance account) amount))

 (defmethod credit ((Account account) amount)
  (incf (account-balance account) amount))


     CL-USER 5 > (setq a (make-instance 'account :account-id "abc123" :balance 10.0))
     #<ACCOUNT 200C05AF>

     CL-USER 6 > (describe a)

     #<ACCOUNT 200C05AF> is an ACCOUNT
     ACCOUNT-ID   "abc123"
     BALANCE    10.0

     CL-USER 7 > (debit a 5)
     5.0

     CL-USER 8 > (describe a)

     #<ACCOUNT 200C05AF> is an ACCOUNT
     ACCOUNT-ID   "abc123"
     BALANCE    5.0


               4/3/11             Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                                                   7
… a very dynamic language

  (defclass Account ()
    ((account-id :accessor account-id :initarg :account-id)                               Existing instances
     (balance :accessor account-balance :initarg :balance)                                are updated when
     (description :accessor account-description :initarg :description))                   the class is redefined
  )


  CL-USER 9 > (describe a)

  #<ACCOUNT 2168DCBF> is an ACCOUNT
  ACCOUNT-ID   "abc123"
  BALANCE     10.0
  DESCRIPTION    #<unbound slot>

  CL-USER 10 > (setf (account-description a) "checking account")
  "checking account"

  CL-USER 11 > (describe a)

  #<ACCOUNT 2168DCBF> is an ACCOUNT
  ACCOUNT-ID   "abc123"
  BALANCE     10.0
  DESCRIPTION   "checking account"




            4/3/11            Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                                              8
Along came Java
o  Provided the masses with:
  n  Garbage collection
  n  Safety, reliability and security


o  And 10+ years passed…




       4/3/11   Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                            Slide 9
Ruby on Rails woke up the Java
community

o  Ruby on Rails:
  n  Significantly more productive
  n  Simplicity of Convention over
      Configuration
o  Motivated the Java community to
   improve: frameworks such as Spring
   improved considerably
o  And…


       4/3/11   Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                            Slide 10
…Highlighted Problems with Java
o  Tedious collection processing
o  Painful object construction code
   n  No literals for collections
   n  Need to use Builders for creating hierarchies
o  Tedious XML processing
o  Types:
   n  Verbose declarations
   n  Generics are complicated
o  Fixed set of control constructs
o  Limited support for DSLs
                          =>
o  Reopened the debate about programming
   languages
o  Dynamic languages became interesting again

         4/3/11     Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                Slide 11
Dynamic languages on the JVM
o  JRuby
   n  http://jruby.codehaus.org
   n  Ruby on the JVM
o  Jython
   n  http://www.jython.org/Project
   n  Python on the JVM
o  Clojure
   n    http://clojure.org/
   n    Functional programming language
   n    Software Transactional Memory
   n    …
o  Groovy
   n  http://groovy.codehaus.org/
   n  Java-compatible, dynamic language


           4/3/11    Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                 Slide 12
Small but growing




     4/3/11   Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                          Slide 13
Static languages on the JVM
   o  (Java)
   o  Fortress
   o  Gosu
   o  Scala
   o  …


http://www.is-research.de/info/vmlanguages/?s=static




                           4/3/11                  Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                                               Slide 14
Scala and Gosu adoption – Smaller
but growing




     4/3/11   Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                          Slide 15
Agenda
o  Evolution of programming languages
o  Favorite Groovy features
o  The frustration of using Groovy
o  Scala: expressiveness and compile-
   time checking
o  The trouble with Scala




      4/3/11   Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                           Slide 16
About Groovy
o  Object-oriented, dynamic language
o  Made popular by the Grails
   framework:
  n  Rails-like productivity
  n  Using robust Java frameworks including
      Spring and Hibernate




   4/3/11     Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                          Slide 17
The good news
o  Easy to learn
  n  Designed to be Java compatible
  n  Interactive Shell
o  Great support for DSLs
o  Closures:
  n  Easy collection processing
  n  New control constructs
o  Property/method missing mechanism
o  Good XML handling
o  Meta-classes for dynamically defining
   new program elements => foundation
   for Grails
       4/3/11   Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                            Slide 18
Intentionally compatible with Java
o  Groovy objects are Java objects
o  Groovy collections are Java collections
o  Groovy can call Java
  n  e.g. leverage all those Java libraries
o  Java can call Groovy, e.g. Groovy
   objects can be:
  n  Spring beans
  n  Hibernate objects
o  Easy to write a module of a project
   Groovy => Easy to incrementally adopt
       4/3/11    Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                             Slide 19
Groovy can look like Java
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

class EC2RequestExecutor {                                                               Java-like syntax

Log logger = LogFactory.getLog(getClass())

 public String calculateRFC2104HMAC(String data, String key) {
   try {
     SecretKeySpec signingKey = new SecretKeySpec(key.getBytes("UTF8"),
                                                     HMAC_SHA1_ALGORITHM)
     Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM)
     mac.init(signingKey)
     byte[] rawHmac = mac.doFinal(data.getBytes())
     return new String(Base64.encodeBase64(rawHmac))
   }
   catch (Exception e) {
     throw new RuntimeException("Failed to generate HMAC : ", e)
 }
}




         4/3/11              Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                                            Slide 20
Groovy is concise and expressive
def configureAsMaster() {
      writeFile fileName: "/etc/my.cnf", templateName: "/templates/master.my.cnf"

       restartService "mysqld"

       exec command: "mysql -u root",
                templateName: "/templates/createSchema.sql",                                 No parens
                templateArgs: [schemaSpec: schemaSpec]                                       Keyword parameters


       executeSchemaScripts()
  }




      class TomcatServer {               tomcatServer.contexts
                                                                                                 No get…()
       def getContexts() {
            webApps.context
        }
      }




              4/3/11             Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                                             Slide 21
An interactive shell




     Great for experimentation and learning



      4/3/11     Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                             Slide 22
Literals for lists and maps

def myList = ["a", "b", "c"]




def params = ['Action': 'RunInstances',
       'MinCount': n.toString(), 'MaxCount': n.toString(),
       ' ImageId': awsProperties."imageId.${instanceType}",
       'KeyName': awsProperties.keyName,
       ' InstanceType': instanceType] + extraParams

def response = requestor.executeRequest(params)



            4/3/11        Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                      Slide 23
GStrings
o  Templating mechanism
o  Embedded Groovy expressions
o  Multi-line strings



def schemaScript = """
  DROP SCHEMA IF EXISTS ${schemaSpec.name};
  CREATE SCHEMA ${schemaSpec.name};
     """




           4/3/11      Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                   Slide 24
Closures – collection processing

 public EC2Server findInstance(String instanceId) {
   def server = servers.find {instanceId == it.instanceId}
   if (server)
     return server
   else throw new RuntimeException(….)
 }

assertEquals(["aa", "bb", "cc"],
  ["a", "b", "c"].collect { elem -> elem + elem }

Simplified collection processing
No ugly anonymous inner classes



            4/3/11       Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                     Slide 25
Closures – new control structures
public class Ssh {

def withTunnel(String publicDnsName, int localPort, int remotePort, Closure closure) {
    SshConnection sshConnection = makeSshConnection(publicDnsName);
    try {
      sshConnection.connect()
      …
      return closure.call()
    } finally {
       sshConnection.close()
    }
  }
}

class EC2 {

String snapshotEbsVolume(String hostName, EbsVolume ebsVolume, String schemaName) {
     int localPort = PortUtil.allocatePort()
     def result = ssh.withTunnel(hostName, localPort, 3306) {
        …
     }
     return (String)result
  }

}



                     4/3/11         Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                                Slide 26
Closures details
groovy:000> x = { println z }
===> groovysh_evaluate
$_run_closure1@658ba380
groovy:000> x.delegate = [z: 99]
===> Foo@564a5320
groovy:000> x.call()
                       Used to resolve
99                     unbound methods and
                       properties
===> null
                                                                            For example, Builders
                                                                            set the delegate


       4/3/11   Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                             Slide 27
Simple reusable code
interface Quackable {
  void quack();
                                                                                    Java:
}                                                                                   complexity of
class QuackableContainer<T extends Quackable>
                                                                                    generics

void add(T element) {
  element.quack();
  …
}
…
}

class QuackableContainer {                  Groovy: just assume that
void add(element) {                         there is a quack() method
  element.quack()
  …
}

           4/3/11       Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                          Slide 28
Method Missing
public class ClassWithMissingMethods {


    def methodMissing(String name, args) {
       Closure method = makeMethodImplementation(name)
       if (method) {
            method(args)
       } else {
            throw new MissingMethodException(name, getClass(), args)
       }
    }

    def makeMethodImplementation(String name) {
       ….
    }

}



    def foo = new ClassWithMissingMethods()
    foo.nonExistentMethod()


                 4/3/11        Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                           Slide 29
Groovy metaclasses
public class StringMetaclassExampleTest extends GroovyTestCase {

    void testAddMethodToString() {
       String.metaClass.doubleString = { -> delegate + delegate }
       String.metaClass.static.makePowerString = { String s, int n ->
          def result = s
          n.times { result = result.doubleString() }
          result
       }
       assertEquals "JavaZoneJavaZone", "JavaZone".doubleString()
       assertEquals "OsloOsloOsloOslo", String.makePowerString("Oslo", 2)
    }
}



     Runtime definition of program elements

     The foundation of many powerful features

                 4/3/11        Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                           Slide 30
XML and GPath expressions
                                                            <RunInstancesResponse>
                                                            …
                                                            <instancesSet>
                                                              <item>
                                                                 <instanceId>i-4ef21327</instanceId>
                                                                 <imageId>ami-3795705e</imageId>
                                                                 <instanceState>
                                                                    <code>0</code>
def client = new HttpClient()                                       <name>pending</name>
…                                                                </instanceState>
def responseStream =                                             <dnsName/>
                                                            …
  getMethod.getResponseBodyAsStream()
                                                            </RunInstancesResponse>
def parser = new XmlParser(false, false)
def response = parser.parseText(responseStream)

def newServers = response.instancesSet.item.collect {
   new EC2Server(this, awsProperties, ssh,
     it.instanceId.text(),
     it.instanceState.name.text())
}


          4/3/11           Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                             Slide 31
Metaclasses enhance JDK classes
groovy:000> new File("/Users/cer/.bashrc").text
===> export PATH=/Users/cer/tools/apache-maven-3.0/bin:
$PATH




groovy:000> l = [1,2,3]
===> [1, 2, 3]
groovy:000> l.getClass()
===> class java.util.ArrayList
groovy:000> l.getClass().metaClass.foo = { delegate.size }
===> groovysh_evaluate$_run_closure1@13a66c87
groovy:000> l.foo()
===> 3

          4/3/11   Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                               Slide 32
Builders
def report(String path, hosts, cpuCount, threadCount) {
  def builder = new groovy.xml.MarkupBuilder(new OutputStreamWriter(new FileOutputStream(path)))
  builder.performanceReport {
   cpus cpuCount
   threads threadCount
   hosts.entrySet().each {hostName, metric ->
    host {
     name hostName                                                     <performanceReport>
     cpuUtil metric.getAverageBusy()                                      <cpus>1</cpus>
    }                                                                     <threads>10</threads>
   }
                                                                          <host>
   requests {
     timing.sort{ a, b-> a.key <=> b.key}.each{ requestName, metric->       <name>database</name>
      request {                                                             <cpuUtil>3.27</cpuUtil>
       name requestName                                                   </host>
       art metric.average()                                               <host>
       errors metric.errorPercentage()
                                                                            <name>tomcat0</name>
      }
     }                                                                      <cpuUtil>94.32</cpuUtil>
   }                                                                      </host>
   def durationValue = (endTime – startTime)/1000.0                    …
   duration durationValue                                                <duration>557.943</duration>
   def tpsValue = transactionCount/ durationValue
                                                                          <tps>10.753786677133686</tps>
   tps tpsValue
   art averageResponseTime()                                              <art>916.6578333333</art>
  }                                                                    </performanceReport>
 }



             4/3/11               Copyright (c) 2011 Chris Richardson. All rights reserved.   Slide 33
Grails plugins use metaclasses
o  Key part of the Grails plugins
o  Plugins can dynamically add methods,
   e.g. GORM persistence methods
o  Injected methods are closures that
   have access to the Spring application
   context
  n  Less need for traditional dependency
      injection
  n  Alternative to AOP static crosscutting or
      (non-POJO) inheritance

       4/3/11   Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                            Slide 34
Grails/GORM persistence methods
 class Customer {                                            Implemented using
    String name                                              metaclasses and method
 }                                                           missing



 Customer c = new Customer("John Doe")

 if (!c.save())
    fail "validation failed: ${c.errors}"

 Customer c2 = Customer.get(c.id)

 c2.delete()

 assertNull Customer.get(c.id)


 def customers = Customer.findAllByName(“Fred”)


           4/3/11       Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                      Slide 35
Commentable plugin example
class CommentableGrailsPlugin {

 def doWithDynamicMethods = { ctx ->

 for(domainClass in application.domainClasses) {
    if(Commentable.class.isAssignableFrom(domainClass.clazz)) {

     domainClass.clazz.metaClass {
      'static' {
        getRecentComments {-> … }
      }

      addComment { poster, String text ->                                        }

      getComments = {-> }
…
}



         4/3/11      Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                     Slide 36
Grails DSL for ORM
 class Customer {

   static transients = ["networth"]

  static mapping = {
      id column: ‘customer_id’                                                  Closures
      table ‘crc_customer’
      columns {

     }
       name column: ‘customer_name’                                             Method
   }                                                                            missing
   def getNetworth() { … }
    }
 ...

         4/3/11     Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                           Slide 37
Grails Controllers -
o  Accessing request/response
o  Databinding
  n  b.properties = params
  n  new B(params)
o  …




       4/3/11   Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                            Slide 38
Agenda
o  Evolution of programming languages
o  Favorite Groovy features
o  The frustration of using Groovy
o  Scala: expressiveness and compile-
   time checking
o  The trouble with Scala




      4/3/11   Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                           Slide 39
Getting complex Groovy code to
work can be frustrating

o  Dynamic language = less information
   for IDE:
  n    Limited compile-time checking
  n    Limited refactorings
  n    Limited completion
  n    No todo list of errors




         4/3/11   Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                              Slide 40
Groovy fans say "write unit tests“
BUT…



                                                                                            Groovy




                                                                          When you have typos
                           versus




                                                                                                Java




     4/3/11   Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                           Slide 41
Unit tests don't always catch errors
  void testPollStatus_discoveringNewServer() {
       mockEC2RequestExecutor.demand.executeRequest {params ->
         ….
       }
       def mockEC2Server = new MockFor(EC2Server.class)
       ….
      mockEC2Server.use {
          mockEC2RequestExecutor.use {
             ec2 = new EC2(awsProperties)
             ec2.pollStatus()                 public class EC2 {
             assertEquals 1, ec2.servers.size()
          }                                     public pollStatus() {
       }                                          def params = ['Action':
    }                                                               'DescribeInstances']
                                                  def p =
                                                   requestor.executeRequest(params)
class EC2RequestExecutor {                         …
                                                }
 public Node executeEc2Request(Map            …
parameters) {                                 }         Method signature changes are
  …                                                     often missed
}

                                                                                          Slide 42
              4/3/11          Copyright (c) 2011 Chris Richardson. All rights reserved.
The trouble with duck typing
o  Interface between components not
   defined in a single place
o  It is scattered throughout callers
o  Difficult to understand
o  Difficult to change




      4/3/11   Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                           Slide 43
The outcome: fear of change

                                Did my tests
                                catch all
                                the obvious errors?




     4/3/11   Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                          Slide 44
Agenda
o  Evolution of programming languages
o  Favorite Groovy features
o  The frustration of using Groovy
o  Scala: expressiveness and
   compile-time checking
o  The trouble with Scala




      4/3/11   Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                           Slide 45
Distilling a language

                                                                                  Builders


                                                                                 Dynamic methods
                                                                                  and properties

                                                                                  Gpath-based
                                                                                  XML handling
                                                                               Dynamic features

                                                                                Static features


                                                                                   Closures


 Groovy
                                                                                   Literals for
                                                                                   collections
                                                                                    GStrings




          4/3/11   Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                                   Slide 46
Scala – a modern (2003) static
language
o  Object-oriented
   n  Pure object-oriented language
   n  All values are objects      “I can honestly say if someone had
   n  Class-based                 shown me the Programming in
                                                                 Scala book by by Martin Odersky,
o  Functional                                                    Lex Spoon & Bill Venners back in
   n  Functions are values                                      2003 I'd probably have never
                                                                 created Groovy.”
   n  Higher-order functions
   n  Currying                                                  James Strachan, Creator of Groovy
                                                                 http://macstrac.blogspot.com/
o  Statically typed                                              2009/04/scala-as-long-term-
   n  Expressive                                                replacement-for.html

   n  Type inference
o  Extensible to support domain specific languages
   n  Methods as infix and postfix operators
   n  Automatic closure construction


          4/3/11      Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                            Slide 47
The good news
o  Fully interoperable with Java
o  Interactive shell
o  Rich, extensible language
o  Closures
o  Good XML processing
o  Named arguments and default
   parameter values
o  Option class avoids null references
o  Traits provide “multiple inheritance”
       4/3/11   Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                            Slide 48
Scala command line




    4/3/11   Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                         Slide 49
Simple example
object StringFunctions {

    def reverseSentence(sentence : String) = sentence.split(" ").reverse.mkString(" ")

}




import org.junit._
import Assert._

@Test
class StringFunctionsTest {

     @Test
     def testReverseSentence() = {
       val input = "Hello New York"
       val expected = "York New Hello"
       assertEquals(expected, StringFunctions.reverseSentence(input))
     }
}


                 4/3/11         Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                            Slide 50
Collection literals example
@Test
class ScalaCollectionExamplesTest {

@Test
 def listExample() = {
   val myList = List(1, 2, 3)
   assertEquals(3, myList.length)
   assertEquals(List(0, 1, 2, 3), 0::myList)
   assertEquals(List(2, 3), myList.tail)
   assertEquals(1, myList.head)
 }

    @Test
    def testMapExampleList() : Unit = {
      val myMap = Map( "x" -> 1, "y" -> 2, "z" -> 3)
      assertEquals(1, myMap("x"))
    }
}




             4/3/11        Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                       Slide 51
Closure example

@Test
class ScalaCollectionExamplesTest {

    @Test
    def testMapList() = {
      val myList = List(1, 2, 3)
      assertEquals(List(2,4,6), myList.map ( x => x * 2))
      assertEquals(List(2,4,6), myList.map ( _ * 2))
    }

    @Test
    def testFilterList() = {
      val myList = List(1, 2, 3)
      assertEquals(List(1, 3), myList.filter( _ % 2 == 1));
    }

}




              4/3/11         Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                         Slide 52
Defining new control constructs 1
class ExampleControlConstructExampleTest {

def withOutputStream[T](f : File)(body : InputStream => T) = {
   val fis = new FileInputStream(f)
   try {
     body(fis)
   } finally {
     fis.close
   }
 }

 @Test
 def withOpenFileExample {
   withOutputStream(new File("/Users/cer/.bashrc")) {fis =>
     println(fis.available)
   }
 }

            4/3/11     Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                   Slide 53
Defining new control constructs 2

                                               By-name parameter




 var assertsEnabled = false

  def myAssert(message : String, assertion : => Boolean) {
    if (assertsEnabled) {
      if (!assertion)
        throw new RuntimeException(message)
    }
  }

  @Test
  def assertionsDisabled = {
    myAssert("never", { println("you will not see me") ; false })
  }




          4/3/11         Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                     Slide 54
Pattern matching
case class Organization(name: String,
     industry: Industry.Value, revenue: Double, assets: Double, liabilities: Double) {

}

object Industry extends Enumeration {
 val Banking, GreenEnergy, IT, Other = Value

}

class LoanApprovalPolicy {

    def isLoanApproved(organization : Organization) = organization match {
      case Organization(_, Industry.GreenEnergy, _, _, _) => true
      case Organization(_, Industry.Banking, _, assets, liabilities)
           if liabilities < assets * 1.5 => true
      case Organization(_, Industry.Banking, _, _, _) => false
      case Organization(_, _, _, assets, liabilities) if assets > liabilities => true
      case _ => false
    }

}


                  4/3/11          Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                              Slide 55
XML generation
@Test
class ScalaXmlExampleTest {

  @Test
  def xmlLiterals() {
   val now = new Date()
   val loanRequestor =
        new Organization("Community Bank, Inc", Industry.Banking, 10, 10, 5)

      val doc = <loanRequest>
             <time>{now.getTime()}</time>
             <requester>{loanRequestor.name}</requester>
             <industry>{loanRequestor.industry}</industry>
             <revenue>{loanRequestor.revenue}</revenue>
             <assets>{loanRequestor.assets}</assets>
             <liabilities>{loanRequestor.liabilities}</liabilities>
       </loanRequest>

      val docAsString : String = doc.toString()
      println(docAsString)
  }



                4/3/11         Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                           Slide 56
XML parsing
class ScalaXmlExampleTest {

  @Test
  def xmlProcessing() {
   val doc = xml.XML.loadString(docAsString)
   assertEquals ("Community Bank, Inc", (doc  "requester").text)

   doc match {
      case <loanRequest>{children @ _* }</loanRequest> =>
       for (x <- children if !x.isInstanceOf[scala.xml.Text]) {
         processChildren(x)
       }
    }
  }

  def processChildren(node : scala.xml.Node ) = {
    node match {
      case <time>{value}</time> => println(value.text)
      case <requester>{value}</requester> => println(value.text)
      …
    }
  }


              4/3/11          Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                          Slide 57
DSL Example
class OrganizationDslTest {
  val o : Organization = (Organization
                       called "Bernie Madoff & Associates"
                       in Industry.Banking
                       withRevenuesOf (10 million)
                       withAssetsOf (10 billion)
                       withLiabilitiesOf (30 billion))

}
     object Organization {
      def called(name : String) = new OrganizationBuilder(name)

      class OrganizationBuilder(name : String) {
        var industry : Industry.Value = null
        def in(industry : Industry.Value) = { this.industry = industry; this }
        def withRevenuesOf(…) = …
        …
        def make()= new Organization(name, industry, revenue, assets, liabilities)
     }

         implicit def organizationBuilderToOrganization(builder : OrganizationBuilder)
         = builder.make
     }

                 4/3/11         Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                            Slide 58
Named arguments and default values

o  Arguments can be named
o  Parameters can have default values
o  Benefits:
  n  Clearer code – Booleans aren’t so evil
  n  Avoids confusing overloaded methods/
      constructors
  n  Less need for the Builder pattern

def foo(x: Int = 10, y: Int = 20) = x * y

foo() should be(200)
foo(y = 30, x = 50) should be(1500)
foo(x = 50) should be(1000)

        4/3/11   Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                             Slide 59
Constructing object hierarchies
  object ExampleSwingApp extends SimpleGUIApplication {

      def top = new MainFrame {
        title = "Example Swing App"                                    Defines method declared
                                                                       abstract in superclass
          val button = new Button {
            text = "press"
          }                                                             Sets property
          contents = button

          var count = 0                                                   Statements/
          listenTo(button)
          reactions += {                                                  expressions that
            case ButtonClicked(b) =>                                      are executed
              count = count + 1
              Console.println(count);
              b.text = "press: " + count                                                 Nesting of new Class {
          }
      }                                                                                  }
                                                                                         mirrors object hierarchy
  }


              4/3/11         Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                                           Slide 60
Pimping existing classes
scala> 5.times { println("Hello") }
<console>:6: error: value times is not a member of Int
      5.times { println("Hello") }
       ^
scala> class IntWithTimes(n: Int) {
    |    def times(body: => Unit) = for (x <- 1 to n) body
    | }
defined class IntWithTimes

scala> implicit def toIntWithTimes(n: Int) = new IntWithTimes(n)
toIntWithTimes: (n: Int)IntWithTimes

scala> 5.times { println("Hello") }
Hello
Hello
Hello                                                                          Implicit conversion
Hello
Hello


              4/3/11     Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                               Slide 61
Implicit parameters 1
@Test
def implicitParamsImplicitVar {
 implicit val defaultValue = 8

    def m(i : Int) (implicit j : Int) = i * j

    assertEquals(48, m(6))

    assertEquals(42, m(6)(7))

}
         4/3/11   Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                              Slide 62
Implicit parameters 2
scala> implicit def toInt : Int = 5
toInt: Int

scala> def m(i : Int) (implicit j : Int) = i * j
m: (i: Int)(implicit j: Int)Int

scala> m(6)
res6: Int = 30

scala> m(6)(7)
res7: Int = 42
        4/3/11   Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                             Slide 63
Null References

                                        My Billion Dollar
                                            mistake




     4/3/11   Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                          Slide 64
Use Options instead of null…
var x : Option[Int] = Some(5)
var y : Option[Int] = None
var z : Option[Int] = Some(7)

assertEquals(5, x.get)
try {
   y.get
   fail
 } catch {
     case _ : NoSuchElementException =>
 }

assertEquals(5, x.getOrElse(10))
assertEquals(10, y.getOrElse(10))


         4/3/11   Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                              Slide 65
… more options
assertEquals(Some(7), x.map(_ + 2))
assertEquals(None, y.map(_ + 2))
assertEquals(None, x.filter(_ == 2))
assertEquals(Some(5), x.filter(_ == 5))

assertEquals(Some(35),
   for (a <- x ; b <- z) yield a * b)



       4/3/11   Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                            Slide 66
Traits
o  Consist of members
  n  methods
  n  fields
  n  types
o  The members can be abstract
o  Multiple traits can be mixed into a class
  n  Alternative to multiple inheritance
  n  Class must define abstract members
o  Uses:
  n  Modularizing applications
  n  Enriching interfaces
  n  Decorating classes

       4/3/11   Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                            Slide 67
RESTful WS using Spring MVC and
  Scala
o  Handling HTTP request                                          Photo management
   n    Extract values from URL
   n    Invoke appropriate handler
                                                                  application
   n    Generate response (XML)
o  Sounds like a job for Scala's                                  GET /album
   n    Pattern matching                                         GET /album/<albumId>
   n    XML support                                              POST /album
                                                                  …


                                                     <album>
                                                        <id>6699</id>
GET /album/6699                                          <photos>…</photos>
                                                         …
                                                     </album>


              4/3/11        Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                        Slide 68
Pattern matching HTTP Requests
  o  Match against
       n  URL – represented as a List
       n  Method
       n  …
  o  Returns XML
request match {
case Request(List("album"), "GET", _, _) =>
   listAlbums
case Request(List("album", albumId), "GET", _, _) =>
    findAlbum(albumId)
}
                                  def findAlbum(albumId : String) = {
                                     def album = albumService.findAlbum(albumId)
                                     <album>
                                      <id>{album.getId()}</id>
                                      <photos>…</photos>
                                      …
                                     </album>
                                   }


                4/3/11        Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                          Slide 69
The Extractor

object Request {
   def unapply(request : HttpServletRequest)
        : Option[ (List[String], String, RequestBody, Binder)]
     ={
     val path =
      List.fromArray(request.getPathInfo().split("/")).tail
     Some(path,
            request.getMethod,
            new RequestBody(request),
            new Binder(request))
   }
}



             4/3/11    Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                   Slide 70
Package as WSEndpoints
trait WsEndpoint extends PartialFunction[HttpServletRequest, Node] {

    def isDefinedAt(request : HttpServletRequest) = wsDispatchRules.isDefinedAt(request)

    def apply(request : HttpServletRequest) = wsDispatchRules(request)

    protected def wsDispatchRules : PartialFunction[HttpServletRequest, Node]

}         @Component
          class AlbumWsEndpoint extends WsEndpoint {

           @Autowired
           def setAlbumService(albumService : AlbumService) = …

           def wsDispatchRules = {
             case Request(List("album", albumId), "GET", _, _) => findAlbum(albumId)
             …
           }

           def findAlbum(albumId : String) = {
              def album = albumService.findAlbum(albumId)
              <album>…</album>
            }

                    4/3/11        Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                              Slide 71
Integrate with Spring MVC 1
@Component
class WsEndpointHandlerMapping extends HandlerMapping {

@Autowired
def setApplicationContext(applicationContext : ApplicationContext) {
  this.applicationContext = applicationContext
}

def endpoints = …applicationContext.getBeansOfType(classOf[WsEndpoint])…

override def getHandler(request : HttpServletRequest) : HandlerExecutionChain = {
   val endpointForRequest = endpoints.find( _.isDefinedAt(request))
   endpointForRequest match {
     case Some(endpoint) => new HandlerExecutionChain(endpoint)
     case None => null
   }
 }



                       Maps HTTP request to WSEndpoint



                  4/3/11        Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                            Slide 72
Integrate with Spring MVC 2
@Component
class WsEndpointHandlerAdapter extends HandlerAdapter {

 override def supports(handler : Object) = {
           handler.isInstanceOf[WsEndpoint]
 }

 override def handle(request : HttpServletRequest ,
                     response : HttpServletResponse , handler : Object ) : ModelAndView = {

     val endpoint = handler.asInstanceOf[WsEndpoint]

     val node = endpoint(request)
     response.setContentType("text/xml")
     response.getWriter.print(node.toString)

     null
 }


                Invokes WSEndpoint and generates HTTP response

                     4/3/11        Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                               Slide 73
Scala and Spring DI
o  Scala and Spring DI play together
o  @Component on Scala classes works
o  @Autowired fields:
  n  Annotation ends up on field and
      generated methods è fail
  n  Need to define and annotate setters




       4/3/11   Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                            Slide 74
Agenda
o  Evolution of programming languages
o  Favorite Groovy features
o  The frustration of using Groovy
o  Scala: expressiveness and compile-
   time checking
o  The trouble with Scala




      4/3/11   Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                           Slide 75
Really bad news
o  Scala IDEs are ok and slowly
   improving BUT have a really really
   long way to go compared to Java
o  Limited refactoring => painful to
   maintain code quality?




      4/3/11   Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                           Slide 76
More bad news…
o  Expressiveness comes from complex
   language features
o  Generics are better than Java but still
   complex
                   BUT
o  The investment in learning these
   features is repaid with every
   application you write


       4/3/11   Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                            Slide 77
…more bad news
o  Lift is not Grails (or Rails)
o  A subset of the Scala community has
   a functional programming background
   => Obsessed with their Monads
o  Implicit conversions make it possible
   to write “incomprehensible” code
o  Scala Collections != Java Collections
  n  BUT Implicit conversions simplify
      interoperability

       4/3/11   Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                            Slide 78
Summary
o  The Java Language has stagnated and
   need to be replaced
o  Dynamic languages are one option
o  But many benefits of those languages
   are due to good language design
o  Scala = expressiveness + static typing
o  But Grails derives much of its power
   from the dynamic features of Groovy
o  Therefore, choose the right tool:
  n  Web application è Use Groovy and Grails
  n  Complex business logic/middleware è
      Consider Scala

       4/3/11   Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                            Slide 79
Summary – Pessimistic view
o  Languages used by mainstream
   developers have improved
   substantially
o  But are those languages that much
   better than the 25+ year old “ivory
   tower” languages: LISP, Smalltalk,
   ML, … ?




      4/3/11   Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                           Slide 80
Final thoughts
o  Learn some languages including
   Haskell
o  Watch the 50 in 50 presentation by
   Guy Steele and Dick Gabriel:
   http://blog.jaoo.dk/2008/11/21/art-
   and-code-obscure-or-beautiful-code/

"Those who cannot remember the past
     are condemned to repeat it”
               http://en.wikipedia.org/wiki/George_Santayana




      4/3/11        Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                                Slide 81
The end
                                    Twitter:


                                    @crichardson


                                    Send email:

                                    chris@chrisrichardson.net




    4/3/11   Copyright (c) 2011 Chris Richardson. All rights reserved.
                                                                         Slide 82

In pursuit of expressivity: Groovy and Scala compared

  • 1.
    In pursuit ofexpressivity: Groovy and Scala compared Chris Richardson Author of POJOs in Action Founder of CloudFoundry.com chris@chrisrichardson.net @crichardson
  • 2.
    Overall presentation goal Examine the good, the bad and the ugly aspects of developing with Groovy and Scala 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 2
  • 3.
    About Chris •  Grew up in England and live in Oakland, CA •  Over 25+ years of software development experience including 14 years of Java •  Speaker at JavaOne, SpringOne, NFJS, JavaPolis, Spring Experience, etc. •  Organize the Oakland JUG ( http://bit.ly/ebjava) and the Groovy Grails meetup (http://bit.ly/oakgrails) http://www.theregister.co.uk/2009/08/19/springsource_cloud_foundry/ 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 3
  • 4.
    Agenda o  Evolution ofprogramming languages o  Favorite Groovy features o  The frustration of using Groovy o  Scala: expressiveness and compile- time checking o  The trouble with Scala 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 4
  • 5.
    Lots of programminglanguages This site lists 8512 languages, complete with 17837 bibliographic records featuring 11064 extracts from those references. It is in effect a family tree of languages with 5445 links, making it not only the biggest programming language family tree around, but also one of the largest idea-genealogical projects undertaken. http://hopl.murdoch.edu.au/ 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 5
  • 6.
    The Elite v.The Masses The Elite Mainstream o  Lisp o  Thought Lisp and n  Invented in the 1960s Smalltalk were: n  Dynamic functional n  Weird (Too different language from Algol) n  Dynamic object-oriented n  Too resource hungry language (mid-1980s) (probably were for the o  Smalltalk time) n  Invented in the 1970s o  So the mainstream used: n  Dynamic object-oriented Pascal, C, C++, …. language o  Not so safe, manual o  Safe, garbage collection, memory management, rich, … primitive, … 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 6
  • 7.
    LISP/CLOS – anearly (late 1980s) dynamic language (defclass Account () ((account-id :accessor account-id :initarg :account-id) (balance :accessor account-balance :initarg :balance)) Develop by adding and ) changing program elements in a running VM (defmethod debit ((Account account) amount) (decf (account-balance account) amount)) (defmethod credit ((Account account) amount) (incf (account-balance account) amount)) CL-USER 5 > (setq a (make-instance 'account :account-id "abc123" :balance 10.0)) #<ACCOUNT 200C05AF> CL-USER 6 > (describe a) #<ACCOUNT 200C05AF> is an ACCOUNT ACCOUNT-ID "abc123" BALANCE 10.0 CL-USER 7 > (debit a 5) 5.0 CL-USER 8 > (describe a) #<ACCOUNT 200C05AF> is an ACCOUNT ACCOUNT-ID "abc123" BALANCE 5.0 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. 7
  • 8.
    … a verydynamic language (defclass Account () ((account-id :accessor account-id :initarg :account-id) Existing instances (balance :accessor account-balance :initarg :balance) are updated when (description :accessor account-description :initarg :description)) the class is redefined ) CL-USER 9 > (describe a) #<ACCOUNT 2168DCBF> is an ACCOUNT ACCOUNT-ID "abc123" BALANCE 10.0 DESCRIPTION #<unbound slot> CL-USER 10 > (setf (account-description a) "checking account") "checking account" CL-USER 11 > (describe a) #<ACCOUNT 2168DCBF> is an ACCOUNT ACCOUNT-ID "abc123" BALANCE 10.0 DESCRIPTION "checking account" 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. 8
  • 9.
    Along came Java o Provided the masses with: n  Garbage collection n  Safety, reliability and security o  And 10+ years passed… 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 9
  • 10.
    Ruby on Railswoke up the Java community o  Ruby on Rails: n  Significantly more productive n  Simplicity of Convention over Configuration o  Motivated the Java community to improve: frameworks such as Spring improved considerably o  And… 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 10
  • 11.
    …Highlighted Problems withJava o  Tedious collection processing o  Painful object construction code n  No literals for collections n  Need to use Builders for creating hierarchies o  Tedious XML processing o  Types: n  Verbose declarations n  Generics are complicated o  Fixed set of control constructs o  Limited support for DSLs => o  Reopened the debate about programming languages o  Dynamic languages became interesting again 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 11
  • 12.
    Dynamic languages onthe JVM o  JRuby n  http://jruby.codehaus.org n  Ruby on the JVM o  Jython n  http://www.jython.org/Project n  Python on the JVM o  Clojure n  http://clojure.org/ n  Functional programming language n  Software Transactional Memory n  … o  Groovy n  http://groovy.codehaus.org/ n  Java-compatible, dynamic language 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 12
  • 13.
    Small but growing 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 13
  • 14.
    Static languages onthe JVM o  (Java) o  Fortress o  Gosu o  Scala o  … http://www.is-research.de/info/vmlanguages/?s=static 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 14
  • 15.
    Scala and Gosuadoption – Smaller but growing 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 15
  • 16.
    Agenda o  Evolution ofprogramming languages o  Favorite Groovy features o  The frustration of using Groovy o  Scala: expressiveness and compile- time checking o  The trouble with Scala 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 16
  • 17.
    About Groovy o  Object-oriented,dynamic language o  Made popular by the Grails framework: n  Rails-like productivity n  Using robust Java frameworks including Spring and Hibernate 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 17
  • 18.
    The good news o Easy to learn n  Designed to be Java compatible n  Interactive Shell o  Great support for DSLs o  Closures: n  Easy collection processing n  New control constructs o  Property/method missing mechanism o  Good XML handling o  Meta-classes for dynamically defining new program elements => foundation for Grails 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 18
  • 19.
    Intentionally compatible withJava o  Groovy objects are Java objects o  Groovy collections are Java collections o  Groovy can call Java n  e.g. leverage all those Java libraries o  Java can call Groovy, e.g. Groovy objects can be: n  Spring beans n  Hibernate objects o  Easy to write a module of a project Groovy => Easy to incrementally adopt 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 19
  • 20.
    Groovy can looklike Java import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; class EC2RequestExecutor { Java-like syntax Log logger = LogFactory.getLog(getClass()) public String calculateRFC2104HMAC(String data, String key) { try { SecretKeySpec signingKey = new SecretKeySpec(key.getBytes("UTF8"), HMAC_SHA1_ALGORITHM) Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM) mac.init(signingKey) byte[] rawHmac = mac.doFinal(data.getBytes()) return new String(Base64.encodeBase64(rawHmac)) } catch (Exception e) { throw new RuntimeException("Failed to generate HMAC : ", e) } } 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 20
  • 21.
    Groovy is conciseand expressive def configureAsMaster() { writeFile fileName: "/etc/my.cnf", templateName: "/templates/master.my.cnf" restartService "mysqld" exec command: "mysql -u root", templateName: "/templates/createSchema.sql", No parens templateArgs: [schemaSpec: schemaSpec] Keyword parameters executeSchemaScripts() } class TomcatServer { tomcatServer.contexts No get…() def getContexts() { webApps.context } } 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 21
  • 22.
    An interactive shell Great for experimentation and learning 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 22
  • 23.
    Literals for listsand maps def myList = ["a", "b", "c"] def params = ['Action': 'RunInstances', 'MinCount': n.toString(), 'MaxCount': n.toString(), ' ImageId': awsProperties."imageId.${instanceType}", 'KeyName': awsProperties.keyName, ' InstanceType': instanceType] + extraParams def response = requestor.executeRequest(params) 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 23
  • 24.
    GStrings o  Templating mechanism o Embedded Groovy expressions o  Multi-line strings def schemaScript = """ DROP SCHEMA IF EXISTS ${schemaSpec.name}; CREATE SCHEMA ${schemaSpec.name}; """ 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 24
  • 25.
    Closures – collectionprocessing public EC2Server findInstance(String instanceId) { def server = servers.find {instanceId == it.instanceId} if (server) return server else throw new RuntimeException(….) } assertEquals(["aa", "bb", "cc"], ["a", "b", "c"].collect { elem -> elem + elem } Simplified collection processing No ugly anonymous inner classes 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 25
  • 26.
    Closures – newcontrol structures public class Ssh { def withTunnel(String publicDnsName, int localPort, int remotePort, Closure closure) { SshConnection sshConnection = makeSshConnection(publicDnsName); try { sshConnection.connect() … return closure.call() } finally { sshConnection.close() } } } class EC2 { String snapshotEbsVolume(String hostName, EbsVolume ebsVolume, String schemaName) { int localPort = PortUtil.allocatePort() def result = ssh.withTunnel(hostName, localPort, 3306) { … } return (String)result } } 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 26
  • 27.
    Closures details groovy:000> x= { println z } ===> groovysh_evaluate $_run_closure1@658ba380 groovy:000> x.delegate = [z: 99] ===> Foo@564a5320 groovy:000> x.call() Used to resolve 99 unbound methods and properties ===> null For example, Builders set the delegate 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 27
  • 28.
    Simple reusable code interfaceQuackable { void quack(); Java: } complexity of class QuackableContainer<T extends Quackable> generics void add(T element) { element.quack(); … } … } class QuackableContainer { Groovy: just assume that void add(element) { there is a quack() method element.quack() … } 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 28
  • 29.
    Method Missing public classClassWithMissingMethods { def methodMissing(String name, args) { Closure method = makeMethodImplementation(name) if (method) { method(args) } else { throw new MissingMethodException(name, getClass(), args) } } def makeMethodImplementation(String name) { …. } } def foo = new ClassWithMissingMethods() foo.nonExistentMethod() 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 29
  • 30.
    Groovy metaclasses public classStringMetaclassExampleTest extends GroovyTestCase { void testAddMethodToString() { String.metaClass.doubleString = { -> delegate + delegate } String.metaClass.static.makePowerString = { String s, int n -> def result = s n.times { result = result.doubleString() } result } assertEquals "JavaZoneJavaZone", "JavaZone".doubleString() assertEquals "OsloOsloOsloOslo", String.makePowerString("Oslo", 2) } } Runtime definition of program elements The foundation of many powerful features 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 30
  • 31.
    XML and GPathexpressions <RunInstancesResponse> … <instancesSet> <item> <instanceId>i-4ef21327</instanceId> <imageId>ami-3795705e</imageId> <instanceState> <code>0</code> def client = new HttpClient() <name>pending</name> … </instanceState> def responseStream = <dnsName/> … getMethod.getResponseBodyAsStream() </RunInstancesResponse> def parser = new XmlParser(false, false) def response = parser.parseText(responseStream) def newServers = response.instancesSet.item.collect { new EC2Server(this, awsProperties, ssh, it.instanceId.text(), it.instanceState.name.text()) } 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 31
  • 32.
    Metaclasses enhance JDKclasses groovy:000> new File("/Users/cer/.bashrc").text ===> export PATH=/Users/cer/tools/apache-maven-3.0/bin: $PATH groovy:000> l = [1,2,3] ===> [1, 2, 3] groovy:000> l.getClass() ===> class java.util.ArrayList groovy:000> l.getClass().metaClass.foo = { delegate.size } ===> groovysh_evaluate$_run_closure1@13a66c87 groovy:000> l.foo() ===> 3 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 32
  • 33.
    Builders def report(String path,hosts, cpuCount, threadCount) { def builder = new groovy.xml.MarkupBuilder(new OutputStreamWriter(new FileOutputStream(path))) builder.performanceReport { cpus cpuCount threads threadCount hosts.entrySet().each {hostName, metric -> host { name hostName <performanceReport> cpuUtil metric.getAverageBusy() <cpus>1</cpus> } <threads>10</threads> } <host> requests { timing.sort{ a, b-> a.key <=> b.key}.each{ requestName, metric-> <name>database</name> request { <cpuUtil>3.27</cpuUtil> name requestName </host> art metric.average() <host> errors metric.errorPercentage() <name>tomcat0</name> } } <cpuUtil>94.32</cpuUtil> } </host> def durationValue = (endTime – startTime)/1000.0 … duration durationValue <duration>557.943</duration> def tpsValue = transactionCount/ durationValue <tps>10.753786677133686</tps> tps tpsValue art averageResponseTime() <art>916.6578333333</art> } </performanceReport> } 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 33
  • 34.
    Grails plugins usemetaclasses o  Key part of the Grails plugins o  Plugins can dynamically add methods, e.g. GORM persistence methods o  Injected methods are closures that have access to the Spring application context n  Less need for traditional dependency injection n  Alternative to AOP static crosscutting or (non-POJO) inheritance 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 34
  • 35.
    Grails/GORM persistence methods class Customer { Implemented using String name metaclasses and method } missing Customer c = new Customer("John Doe") if (!c.save()) fail "validation failed: ${c.errors}" Customer c2 = Customer.get(c.id) c2.delete() assertNull Customer.get(c.id) def customers = Customer.findAllByName(“Fred”) 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 35
  • 36.
    Commentable plugin example classCommentableGrailsPlugin { def doWithDynamicMethods = { ctx -> for(domainClass in application.domainClasses) { if(Commentable.class.isAssignableFrom(domainClass.clazz)) { domainClass.clazz.metaClass { 'static' { getRecentComments {-> … } } addComment { poster, String text -> } getComments = {-> } … } 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 36
  • 37.
    Grails DSL forORM class Customer { static transients = ["networth"] static mapping = { id column: ‘customer_id’ Closures table ‘crc_customer’ columns { } name column: ‘customer_name’ Method } missing def getNetworth() { … } } ... 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 37
  • 38.
    Grails Controllers - o Accessing request/response o  Databinding n  b.properties = params n  new B(params) o  … 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 38
  • 39.
    Agenda o  Evolution ofprogramming languages o  Favorite Groovy features o  The frustration of using Groovy o  Scala: expressiveness and compile- time checking o  The trouble with Scala 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 39
  • 40.
    Getting complex Groovycode to work can be frustrating o  Dynamic language = less information for IDE: n  Limited compile-time checking n  Limited refactorings n  Limited completion n  No todo list of errors 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 40
  • 41.
    Groovy fans say"write unit tests“ BUT… Groovy When you have typos versus Java 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 41
  • 42.
    Unit tests don'talways catch errors void testPollStatus_discoveringNewServer() { mockEC2RequestExecutor.demand.executeRequest {params -> …. } def mockEC2Server = new MockFor(EC2Server.class) …. mockEC2Server.use { mockEC2RequestExecutor.use { ec2 = new EC2(awsProperties) ec2.pollStatus() public class EC2 { assertEquals 1, ec2.servers.size() } public pollStatus() { } def params = ['Action': } 'DescribeInstances'] def p = requestor.executeRequest(params) class EC2RequestExecutor { … } public Node executeEc2Request(Map … parameters) { } Method signature changes are … often missed } Slide 42 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved.
  • 43.
    The trouble withduck typing o  Interface between components not defined in a single place o  It is scattered throughout callers o  Difficult to understand o  Difficult to change 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 43
  • 44.
    The outcome: fearof change Did my tests catch all the obvious errors? 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 44
  • 45.
    Agenda o  Evolution ofprogramming languages o  Favorite Groovy features o  The frustration of using Groovy o  Scala: expressiveness and compile-time checking o  The trouble with Scala 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 45
  • 46.
    Distilling a language Builders Dynamic methods and properties Gpath-based XML handling Dynamic features Static features Closures Groovy Literals for collections GStrings 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 46
  • 47.
    Scala – amodern (2003) static language o  Object-oriented n  Pure object-oriented language n  All values are objects “I can honestly say if someone had n  Class-based shown me the Programming in Scala book by by Martin Odersky, o  Functional Lex Spoon & Bill Venners back in n  Functions are values 2003 I'd probably have never created Groovy.” n  Higher-order functions n  Currying James Strachan, Creator of Groovy http://macstrac.blogspot.com/ o  Statically typed 2009/04/scala-as-long-term- n  Expressive replacement-for.html n  Type inference o  Extensible to support domain specific languages n  Methods as infix and postfix operators n  Automatic closure construction 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 47
  • 48.
    The good news o Fully interoperable with Java o  Interactive shell o  Rich, extensible language o  Closures o  Good XML processing o  Named arguments and default parameter values o  Option class avoids null references o  Traits provide “multiple inheritance” 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 48
  • 49.
    Scala command line 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 49
  • 50.
    Simple example object StringFunctions{ def reverseSentence(sentence : String) = sentence.split(" ").reverse.mkString(" ") } import org.junit._ import Assert._ @Test class StringFunctionsTest { @Test def testReverseSentence() = { val input = "Hello New York" val expected = "York New Hello" assertEquals(expected, StringFunctions.reverseSentence(input)) } } 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 50
  • 51.
    Collection literals example @Test classScalaCollectionExamplesTest { @Test def listExample() = { val myList = List(1, 2, 3) assertEquals(3, myList.length) assertEquals(List(0, 1, 2, 3), 0::myList) assertEquals(List(2, 3), myList.tail) assertEquals(1, myList.head) } @Test def testMapExampleList() : Unit = { val myMap = Map( "x" -> 1, "y" -> 2, "z" -> 3) assertEquals(1, myMap("x")) } } 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 51
  • 52.
    Closure example @Test class ScalaCollectionExamplesTest{ @Test def testMapList() = { val myList = List(1, 2, 3) assertEquals(List(2,4,6), myList.map ( x => x * 2)) assertEquals(List(2,4,6), myList.map ( _ * 2)) } @Test def testFilterList() = { val myList = List(1, 2, 3) assertEquals(List(1, 3), myList.filter( _ % 2 == 1)); } } 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 52
  • 53.
    Defining new controlconstructs 1 class ExampleControlConstructExampleTest { def withOutputStream[T](f : File)(body : InputStream => T) = { val fis = new FileInputStream(f) try { body(fis) } finally { fis.close } } @Test def withOpenFileExample { withOutputStream(new File("/Users/cer/.bashrc")) {fis => println(fis.available) } } 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 53
  • 54.
    Defining new controlconstructs 2 By-name parameter var assertsEnabled = false def myAssert(message : String, assertion : => Boolean) { if (assertsEnabled) { if (!assertion) throw new RuntimeException(message) } } @Test def assertionsDisabled = { myAssert("never", { println("you will not see me") ; false }) } 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 54
  • 55.
    Pattern matching case classOrganization(name: String, industry: Industry.Value, revenue: Double, assets: Double, liabilities: Double) { } object Industry extends Enumeration { val Banking, GreenEnergy, IT, Other = Value } class LoanApprovalPolicy { def isLoanApproved(organization : Organization) = organization match { case Organization(_, Industry.GreenEnergy, _, _, _) => true case Organization(_, Industry.Banking, _, assets, liabilities) if liabilities < assets * 1.5 => true case Organization(_, Industry.Banking, _, _, _) => false case Organization(_, _, _, assets, liabilities) if assets > liabilities => true case _ => false } } 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 55
  • 56.
    XML generation @Test class ScalaXmlExampleTest{ @Test def xmlLiterals() { val now = new Date() val loanRequestor = new Organization("Community Bank, Inc", Industry.Banking, 10, 10, 5) val doc = <loanRequest> <time>{now.getTime()}</time> <requester>{loanRequestor.name}</requester> <industry>{loanRequestor.industry}</industry> <revenue>{loanRequestor.revenue}</revenue> <assets>{loanRequestor.assets}</assets> <liabilities>{loanRequestor.liabilities}</liabilities> </loanRequest> val docAsString : String = doc.toString() println(docAsString) } 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 56
  • 57.
    XML parsing class ScalaXmlExampleTest{ @Test def xmlProcessing() { val doc = xml.XML.loadString(docAsString) assertEquals ("Community Bank, Inc", (doc "requester").text) doc match { case <loanRequest>{children @ _* }</loanRequest> => for (x <- children if !x.isInstanceOf[scala.xml.Text]) { processChildren(x) } } } def processChildren(node : scala.xml.Node ) = { node match { case <time>{value}</time> => println(value.text) case <requester>{value}</requester> => println(value.text) … } } 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 57
  • 58.
    DSL Example class OrganizationDslTest{ val o : Organization = (Organization called "Bernie Madoff & Associates" in Industry.Banking withRevenuesOf (10 million) withAssetsOf (10 billion) withLiabilitiesOf (30 billion)) } object Organization { def called(name : String) = new OrganizationBuilder(name) class OrganizationBuilder(name : String) { var industry : Industry.Value = null def in(industry : Industry.Value) = { this.industry = industry; this } def withRevenuesOf(…) = … … def make()= new Organization(name, industry, revenue, assets, liabilities) } implicit def organizationBuilderToOrganization(builder : OrganizationBuilder) = builder.make } 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 58
  • 59.
    Named arguments anddefault values o  Arguments can be named o  Parameters can have default values o  Benefits: n  Clearer code – Booleans aren’t so evil n  Avoids confusing overloaded methods/ constructors n  Less need for the Builder pattern def foo(x: Int = 10, y: Int = 20) = x * y foo() should be(200) foo(y = 30, x = 50) should be(1500) foo(x = 50) should be(1000) 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 59
  • 60.
    Constructing object hierarchies object ExampleSwingApp extends SimpleGUIApplication { def top = new MainFrame { title = "Example Swing App" Defines method declared abstract in superclass val button = new Button { text = "press" } Sets property contents = button var count = 0 Statements/ listenTo(button) reactions += { expressions that case ButtonClicked(b) => are executed count = count + 1 Console.println(count); b.text = "press: " + count Nesting of new Class { } } } mirrors object hierarchy } 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 60
  • 61.
    Pimping existing classes scala>5.times { println("Hello") } <console>:6: error: value times is not a member of Int 5.times { println("Hello") } ^ scala> class IntWithTimes(n: Int) { | def times(body: => Unit) = for (x <- 1 to n) body | } defined class IntWithTimes scala> implicit def toIntWithTimes(n: Int) = new IntWithTimes(n) toIntWithTimes: (n: Int)IntWithTimes scala> 5.times { println("Hello") } Hello Hello Hello Implicit conversion Hello Hello 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 61
  • 62.
    Implicit parameters 1 @Test defimplicitParamsImplicitVar { implicit val defaultValue = 8 def m(i : Int) (implicit j : Int) = i * j assertEquals(48, m(6)) assertEquals(42, m(6)(7)) } 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 62
  • 63.
    Implicit parameters 2 scala>implicit def toInt : Int = 5 toInt: Int scala> def m(i : Int) (implicit j : Int) = i * j m: (i: Int)(implicit j: Int)Int scala> m(6) res6: Int = 30 scala> m(6)(7) res7: Int = 42 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 63
  • 64.
    Null References My Billion Dollar mistake 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 64
  • 65.
    Use Options insteadof null… var x : Option[Int] = Some(5) var y : Option[Int] = None var z : Option[Int] = Some(7) assertEquals(5, x.get) try { y.get fail } catch { case _ : NoSuchElementException => } assertEquals(5, x.getOrElse(10)) assertEquals(10, y.getOrElse(10)) 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 65
  • 66.
    … more options assertEquals(Some(7),x.map(_ + 2)) assertEquals(None, y.map(_ + 2)) assertEquals(None, x.filter(_ == 2)) assertEquals(Some(5), x.filter(_ == 5)) assertEquals(Some(35), for (a <- x ; b <- z) yield a * b) 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 66
  • 67.
    Traits o  Consist ofmembers n  methods n  fields n  types o  The members can be abstract o  Multiple traits can be mixed into a class n  Alternative to multiple inheritance n  Class must define abstract members o  Uses: n  Modularizing applications n  Enriching interfaces n  Decorating classes 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 67
  • 68.
    RESTful WS usingSpring MVC and Scala o  Handling HTTP request Photo management n  Extract values from URL n  Invoke appropriate handler application n  Generate response (XML) o  Sounds like a job for Scala's GET /album n  Pattern matching GET /album/<albumId> n  XML support POST /album … <album> <id>6699</id> GET /album/6699 <photos>…</photos> … </album> 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 68
  • 69.
    Pattern matching HTTPRequests o  Match against n  URL – represented as a List n  Method n  … o  Returns XML request match { case Request(List("album"), "GET", _, _) => listAlbums case Request(List("album", albumId), "GET", _, _) => findAlbum(albumId) } def findAlbum(albumId : String) = { def album = albumService.findAlbum(albumId) <album> <id>{album.getId()}</id> <photos>…</photos> … </album> } 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 69
  • 70.
    The Extractor object Request{ def unapply(request : HttpServletRequest) : Option[ (List[String], String, RequestBody, Binder)] ={ val path = List.fromArray(request.getPathInfo().split("/")).tail Some(path, request.getMethod, new RequestBody(request), new Binder(request)) } } 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 70
  • 71.
    Package as WSEndpoints traitWsEndpoint extends PartialFunction[HttpServletRequest, Node] { def isDefinedAt(request : HttpServletRequest) = wsDispatchRules.isDefinedAt(request) def apply(request : HttpServletRequest) = wsDispatchRules(request) protected def wsDispatchRules : PartialFunction[HttpServletRequest, Node] } @Component class AlbumWsEndpoint extends WsEndpoint { @Autowired def setAlbumService(albumService : AlbumService) = … def wsDispatchRules = { case Request(List("album", albumId), "GET", _, _) => findAlbum(albumId) … } def findAlbum(albumId : String) = { def album = albumService.findAlbum(albumId) <album>…</album> } 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 71
  • 72.
    Integrate with SpringMVC 1 @Component class WsEndpointHandlerMapping extends HandlerMapping { @Autowired def setApplicationContext(applicationContext : ApplicationContext) { this.applicationContext = applicationContext } def endpoints = …applicationContext.getBeansOfType(classOf[WsEndpoint])… override def getHandler(request : HttpServletRequest) : HandlerExecutionChain = { val endpointForRequest = endpoints.find( _.isDefinedAt(request)) endpointForRequest match { case Some(endpoint) => new HandlerExecutionChain(endpoint) case None => null } } Maps HTTP request to WSEndpoint 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 72
  • 73.
    Integrate with SpringMVC 2 @Component class WsEndpointHandlerAdapter extends HandlerAdapter { override def supports(handler : Object) = { handler.isInstanceOf[WsEndpoint] } override def handle(request : HttpServletRequest , response : HttpServletResponse , handler : Object ) : ModelAndView = { val endpoint = handler.asInstanceOf[WsEndpoint] val node = endpoint(request) response.setContentType("text/xml") response.getWriter.print(node.toString) null } Invokes WSEndpoint and generates HTTP response 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 73
  • 74.
    Scala and SpringDI o  Scala and Spring DI play together o  @Component on Scala classes works o  @Autowired fields: n  Annotation ends up on field and generated methods è fail n  Need to define and annotate setters 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 74
  • 75.
    Agenda o  Evolution ofprogramming languages o  Favorite Groovy features o  The frustration of using Groovy o  Scala: expressiveness and compile- time checking o  The trouble with Scala 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 75
  • 76.
    Really bad news o Scala IDEs are ok and slowly improving BUT have a really really long way to go compared to Java o  Limited refactoring => painful to maintain code quality? 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 76
  • 77.
    More bad news… o Expressiveness comes from complex language features o  Generics are better than Java but still complex BUT o  The investment in learning these features is repaid with every application you write 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 77
  • 78.
    …more bad news o Lift is not Grails (or Rails) o  A subset of the Scala community has a functional programming background => Obsessed with their Monads o  Implicit conversions make it possible to write “incomprehensible” code o  Scala Collections != Java Collections n  BUT Implicit conversions simplify interoperability 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 78
  • 79.
    Summary o  The JavaLanguage has stagnated and need to be replaced o  Dynamic languages are one option o  But many benefits of those languages are due to good language design o  Scala = expressiveness + static typing o  But Grails derives much of its power from the dynamic features of Groovy o  Therefore, choose the right tool: n  Web application è Use Groovy and Grails n  Complex business logic/middleware è Consider Scala 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 79
  • 80.
    Summary – Pessimisticview o  Languages used by mainstream developers have improved substantially o  But are those languages that much better than the 25+ year old “ivory tower” languages: LISP, Smalltalk, ML, … ? 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 80
  • 81.
    Final thoughts o  Learnsome languages including Haskell o  Watch the 50 in 50 presentation by Guy Steele and Dick Gabriel: http://blog.jaoo.dk/2008/11/21/art- and-code-obscure-or-beautiful-code/ "Those who cannot remember the past are condemned to repeat it” http://en.wikipedia.org/wiki/George_Santayana 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 81
  • 82.
    The end Twitter: @crichardson Send email: chris@chrisrichardson.net 4/3/11 Copyright (c) 2011 Chris Richardson. All rights reserved. Slide 82