The Scala Experience Safe Programming Can Be Fun!
Total Page:16
File Type:pdf, Size:1020Kb
The Scala Programming Language Mooly Sagiv Slides taken from Martin Odersky (EPFL) Donna Malayeri (CMU) Hila Peleg (Technion) Modern Functional Programming • Higher order • Modules • Pattern matching • Statically typed with type inference • Two viable alternatives • Haskel • Pure lazy evaluation and higher order programming leads to Concise programming • Support for domain specific languages • I/O Monads • Type classes • ML/Ocaml/F# • Eager call by value evaluation • Encapsulated side-effects via references • [Object orientation] Then Why aren’t FP adapted? • Education • Lack of OO support • Subtyping increases the complexity of type inference • Programmers seeks control on the exact implementation • Imperative programming is natural in certain situations Why Scala? (Coming from OCaml) • Runs on the JVM/.NET • Can use any Java code in Scala • Combines functional and imperative programming in a smooth way • Effective libraries • Inheritance • General modularity mechanisms The Java Programming Language • Designed by Sun 1991-95 • Statically typed and type safe • Clean and Powerful libraries • Clean references and arrays • Object Oriented with single inheritance • Interfaces with multiple inheritance • Portable with JVM • Effective JIT compilers • Support for concurrency • Useful for Internet Java Critique • Downcasting reduces the effectiveness of static type checking • Many of the interesting errors caught at runtime • Still better than C, C++ • Huge code blowouts • Hard to define domain specific knowledge • A lot of boilerplate code • Sometimes OO stands in our way • Generics only partially helps • Array subtype does not work Why Scala? (Coming from Java/C++) • Runs on the JVM/.NET • Can use any Java code in Scala • Almost as fast as Java (within 10%) • Much shorter code • Odersky reports 50% reduction in most code over Java • Local type inference • Fewer errors • No Null Pointer problems • More flexibility • As many public classes per source file as you want • Operator overloading Scala • Designed and implemented by Martin Odersky [2001-] • Motivated towards “ordinary” programmers • Scalable version of software • Focused on abstractions, composition, decomposition • Unifies OOP and FP • Exploit FP on a mainstream platform • Higher order functions • Pattern matching • Lazy evaluation • Interoperates with JVM and .NET • Better support for component software • Much smaller code Scala • Scala is an object-oriented and functional language which is completely interoperable with Java (.NET) • Remove some of the more arcane constructs of these environments and adds instead: (1) a uniform object model, (2) pattern matching and higher-order functions, (3) novel ways to abstract and compose programs Getting Started in Scala • scala • Runs compiled scala code • Or without arguments, as an interpreter! • scalac - compiles • fsc - compiles faster! (uses a background server to minimize startup time) • Go to scala-lang.org for downloads/documentation • Read Scala: A Scalable Language (see http://www.artima.com/scalazine/articles/scalable- language.html ) Plan Motivation • Scala vs. Java • Modularity • Discussion Features of Scala • Scala is both functional and object-oriented • every value is an object • every function is a value--including methods • Scala is statically typed • includes a local type inference system: • in Java 1.5: Pair<Integer, String> p = new Pair<Integer, String>(1, "Scala"); • in Scala: val p = new MyPair(1, "scala"); Basic Scala OCaml • Use var to declare variables: let x = ref 3 in var x = 3; x := !x + 4 x += 4; • Use val to declare values (final vars) val y = 3; y += 4; // error • Notice no types, but it is statically typed var x = 3; x = “hello world”; // error • Type annotations: var x : Int = 3; Scala is interoperable object instead of static Scala programs interoperate var: Type instead of Type var seamlessly with Java class members libraries: object Example1 { • Method calls • Field accesses def main(args: Array[String]) { • Class inheritance val b = new StringBuilder() • Interface implementation for (i 0 until args.length) { all work as in Java if (i > 0) b.append(" ") Scala programs compile to JVM bytecodes b.append(args(i).toUpperCase) } Scala’s syntax resembles Console.println(b.toString) Java’s, but there are also some } differences } Scala’s version of the extended for loop Arrays are indexed args(i) (use <- as an alias for ) instead of args[i] Scala is functional Arraysmap isare a methodinstances of of Array sequenceswhich with appliesmap theand function mkString onmethods its right The last program can also to each array element object Example2 { be written in a completely def main(args: Array[String]) { different style: println(args. map (_.toUpperCase) . • Treat arrays as instances of mkString " ") general sequence } abstractions } • Use higher-order functions instead of loops A closure which applies the mkString is a methodtoUpperCase of Arraymethodwhich to its String forms a string of all elements withargument a given separator between them mk_string map (fun x -> toUpperCase(x), args), " " Functions, Mapping, Filtering • Defining lambdas – nameless functions (types sometimes needed) val f = x :Int => x + 42; f is now a mapping int-> int • Closures! A way to haul around state var y = 3; val g = {x : Int => y += 1; x+y; } • Maps (and a cool way to do some functions) List(1,2,3).map(_+10).foreach(println) • Filtering (and ranges!) (1 to 100). filter (_ % 7 == 3). foreach (println) • (Feels a bit like doing unix pipes?) Scala is concise Scala’s syntax is lightweight var capital = Map( "US" "Washington", and concise "France" "paris", Contributors: "Japan" "tokyo" ) • type inference capital += ( "Russia" "Moskow" ) • lightweight classes for ( (country, city) capital ) • extensible API’s capital += ( country city.capitalize ) • closures as assert ( capital("Japan") == "Tokyo" ) control abstractions Average reduction in LOC wrt Java: ≥ 2 due to concise syntax and better abstraction capabilities Big or small? Every language design faces the tension whether it Scala adds Scala removes should be big or small: + a pure object - static members • Big is good: expressive, system easy to use • Small is good: elegant, + operator - special treatment of easy to learn overloading primitive types Can a language be both big + closures as control - break, continue and small? abstractions + mixin composition - special treatment of Scala’s approach: with traits interfaces concentrate on abstraction and composition + abstract type - wildcards capabilities instead of basic members language constructs + pattern matching The Scala design Scala strives for the Scala unifies tightest possible integration of OOP and • algebraic data types FP in a statically typed with class language hierarchies, • functions with This continues to have objects unexpected consequences Has some benefits with concurrency ADTs are class hierarchies Many functional Object-oriented languages have algebraic programmers object: data types and pattern • ADTs are not extensible, matching • ADTs violate the purity of the OO data model Concise and canonical • Pattern matching breaks encapsulation manipulation of data • and it violates structures representation independence! Pattern matching in Scala The case modifier of an object or class means you can pattern match on it Here's a a set of abstract class Tree[T] definitions describing case object Empty extends Tree binary trees: case class Binary(elem: T, left: Tree[T], right: Tree[T]) extends Tree And here's an inorder traversal of def inOrder [T] ( t: Tree[T] ): List[T] = t match { case Empty => List() binary trees: case Binary(e, l, r) => inOrder(l) ::: List(e) ::: inOrder(r) This design keeps } • purity: all cases are classes or objects • extensibility: you can define more cases elsewhere • encapsulation: only parameters of case classes are revealed • representation independence using extractors [Beyond the scope of the course] Pattern Scala vs. OCaml abstract class Tree[T] case object Empty extends Tree case class Binary(elem: T, left: Tree[T], right: Tree[T]) extends Tree def inOrder [T] ( t: Tree[T] ): List[T] = t match { case Empty => List() case Binary(e, l, r) => inOrder(l) ::: List(e) ::: inOrder(r) } type Tree = Empty | Binary of Element * Tree * Tree let rec InOrder (t : tree) = match t with | Empty -> [] | Binary (element, left, right) -> List.append( List.append(inOrder(left), [element]), InOrder(right)) Mutable vs. Immutable Data Structures • Basic data structures in Scala are immutable • Operations will copy (if they must) x a b c y y = x.drop(2) z = x.map(_ + "h") z ah bh ch x a b c y • Many positive consequences Mutable vs. Immutable • Mutable and immutable collections are not the same type hierarchy! • Have to copy the collection to change back and forth, can’t cast x.toList List MutableList More features • Supports lightweight syntax for anonymous functions, higher-order functions, nested functions, currying • Integration with XML • can write XML directly in Scala program • can convert XML DTD into Scala class definitions • Support for regular expression patterns Other features • Allows defining new control structures without using macros, and while maintaining static typing • Any function can be used as an infix or postfix operator • Semicolon inference • Can define methods named +, <= or :: Automatic Closure Construction • Allows programmers to make their own control structures • Can tag the parameters of methods with the modifier def • When method is called, the actual