Programming in Scala

  1. A Scalable Language
    • Intro
      • “scalable language”
      • mix of oop and fp
      • flexible: good for scripts or complex systems
    • A language that grows on you
      • feel of modern scripting language
      • can extend and adapt library abstractions
      • like bazaar: designed and extended by users
      • can define classes that look like built-in types e.g. BigInt
      • can build customized operations e.g. actor ops like “loop” or “receive”
      • address and extend for new domains but feel like native language
    • What makes scala scalable?
      • combinations of oop + fp
      • a function value is an object
      • OOP: put data and operations into some container
      • fp: 1) functions are first class values that be set, passed as args, defined anonymously 2) immutability. take input and transform, return without side-effects
      • scala does allow imperative programming with mutable data and side effects
    • Why Scala?
      • compatible with Java code
      • implicits help deal with interoperability e.g. StringToInt
      • more concise vs java and gives you tools to define powerful libraries
      • build and compose own types
      • static typing guarantees
      • easier to refactor
      • acts as documentation
      • type inference can remove duplicative type info
      • client app code that glues together libraries can use very little explicit types
      • library code usually more because allow flexible usage patterns
    • Scala’s roots
      • combines many existing ideas
      • syntax, types, class libraries and execution model from Java
      • uniform object model from Smalltalk and Ruby
      • FP from SML, OCaml, F#
      • higher order functions from ML/Haskell
      • actor-based concurrency from Erlang
  2. First Steps in Scala
    • Scala Interpreter
      • use “scala” command for interactive shell
      • all of java’s primitive types have a corresponding one in scala
    • variables
      • type inference good, but explicit may be more self-documenting
    • functions
      • compiler does not infer function parameter types
      • func defines an expression that returns a value
      • sometimes compiler can infer return type but not for recursive (but should include)
      • unit type is like void (for side-effects)
    • scripts
      • can run as scripts
      • access args with “args” array
    • loop
      • can use while loop but encouraged to favor for loops
  3. Next Steps in Scala
    • Parameterize arrays with types
      • Array[String]
      • “()” is calling apply method
      • “()” with assignment calls update e.g. “arr(0) = “Hello” is “arr.update(0, “Hello”)”
      • scala treats everything as objects with methods
      • “Array(“zero”, “one”)” calling factory method apply that takes variable args
    • Use lists
      • encourage pure functions
      • scala lists are immutable and methods return new lists
      • “::” cons preprends element to list
      • “:::” combines two lists
      • method names ending in colon are invoked by the right operand e.g. “1::b” is “b.::(1)”
      • “:+” append, but linear operation prefer prepend and reverse
    • Tuples
      • can have different types of them
      • immutable
      • access with “._1”
      • (99, “Balloon”) : Tuple2[Int,String]
      • can’t use apply because can return different types
    • Sets and Maps
      • scala provides mutable and immutable versions
      • if you want mutable set import
      • can also import concrete class i.e. HashSet
      • default uses immutable
    • Learn to recognize functional style
      • vars vs vals
      • functional version is usually cleaner and concise and less error-prone
      • side effects or functions not returning any values should have Unit type
      • functions without side-effects are easier to test
  4. Classes and Objects
    • Classes, fields and methods
      • can make fields private, they are public by default
      • avoid explicit / multiple returns
      • can leave out “=” in function definition for functions only with side effects
    • Semicolons usually not required
    • Singleton Objects
      • classes cannot have static members
      • have singleton objects instead (companion object)
      • companion class and object can access each other’s private members
    • A Scala Application
      • object with main method taking Array[String] and type Unit
      • implicitly imports java.lang, scala and Predef object
      • scala file ending in expression can be run as a script
      • “scalac” can be slow, but can use “fsc” to start daemon
      • compilation produces java class files
    • The Application Trait
      • scala provides a trait “Application” that you can use
      • only use if simple, no args, and single threaded
  5. Basic Types and Operations
    • Basic Types
      • Byte, Short, Int, Long, Char, String, Float, Double, Bool
    • Literals
      • integral nums can be defined with decimal, hex, or octal (obsolete)
      • 0x (hex), nL (long)
      • float 32-point, double 64-point
      • char single quote ‘A’, ‘\101’ (A), ‘\u0041’
      • unicode can appear anywhere in program (bad)
      • raw strings use triple quotes and don’t need escaping
    • Operators are methods
      • operators not special syntax
      • prefix op have “unary_” in method name (+,-,!,~)
    • Arithmetic: +, -, *, /
    • Relational and Logical: >, <, >=, !, &&, ||
      • logical operators short circuit
    • Bitwise: &, |, ^, ~ (inverts each bit), <<, >>
    • Object Equality usually works the way you want e.g. “h” + “h” == “hh”
    • operator precedence and associations
      • exists precedence */%, +-, :, =!, <>, &, ^, |
      • usually good style to be more explicit
    • Rich Wrappers
      • for each type exists a rich wrapper that provides additional methods
  6. Functional Objects
    • Specification for class “Rational”
      • numerator and denom. can support basic operations
      • no mutable state
      • returns new rational number
    • Construction
      • many benefits to immutable objects
      • could be slow for copying large objects graphs
    • Reimplementing toString
      • default prints class@hex
      • override to implement own
    • Preconditions
      • can define preconditions in primary constructor
      • “require (d != 0)” right under “class Rational(…) {“
    • Adding fields
      • to access class params, need to make fields
    • Self References
      • this refers to object instance of currently executing method
      • sometimes required if returning object
    • Auxiliary constructors
      • start with def this()
      • all auxiliary constructors must call other constructors
    • Private fields and methods prepend with private
    • Define operators
      • def +, def *
    • Identifiers
      • use camelCase by convention
      • scala convention for constants is camel case with capitalized first char
    • Implicit conversions
      • r*2 works, but not 2*r
      • define implicit “implicit def intToRational(x:Int) new Rat(x)”
      • must be in scope to work
    • Caution
      • can result in concise an easy to understand code OR
      • may be non-obvious to client programmers what implicit conversions are being applied
      • operator methods can be more concise, but only readable if client programmers know meaning
      • optimize for readable and understandable client code
  7. Built-in Control Structures
    • Intro
      • if, while, for, try, match
      • fp: returns values
    • If expressions
      • val f = if(True) “a” else “b”
      • vals easier to understand because don’t change
      • equational reasoning can replace variable with expression / easier to read/refactor
    • while loops
      • while/do while called “loops”
      • usually left out in pure functional languages
      • be suspicious of while loops
    • for expressions
      • for (file <- filesHere)
      • 1 to 4
      • 1 until 4
      • don’t iterate with index often, usually iterate on items directly
    • filtering
      • for (f <- files if cond)
      • can use multiple generators for nested iteration
      • can assign variables midstream in for expression
      • use yield to create new collection dependent on collection type you iterated on
      • yield goes outside of block
    • exception handling w/ try
      • thrown exception type is Nothing but will never get returned
      • catch with pattern matching
      • finally clauses usually do some cleanup
      • don’t return values in finally
      • treat as way to ensure some side effects happens
    • match expressions
      • select a number of alternatives
      • results in a value
    • living w/o break and continue
      • left out because do not mesh well with function literals
      • replace every continue with if and every break with a boolean var
      • can use recursion instead of looping
      • if absolutely required can use Breaks class and call break on breakable block
    • variable scope
      • each brace defines new scope
      • can define same variable inside new scope but not in same scope
      • in repl new scope defined each line so can reuse variables
    • refactoring imperative-style code
      • make a function that returns a value
      • use vals, for expressions, helper functions and calls mkString
  8. Functions and Closures
    • Methods
      • most common as part of an object
    • local functions
      • hide helper functions that do not make sense individually
      • can use private like Java
      • can also define func in another func / don’t need to pass parameters to function (closure)
    • first-class functions
      • func literals compiled to a class that instantiates to a function value
      • function literal exists in source code
      • function values exists as objects at run time
    • short-forms of function literals
      • can leave off param type
      • leave out parentheses x => x >0
    • placeholder syntax
      • _>0 or _ + _
      • sometimes compiler can’t figure out type
      • f: (_: Int) + (_: Int)
      • multiple underscore means multiple parameters but can’t use parameter more than once
    • partially applied functions
      • can also use “_” for entire param list
      • can’t assign or pass around method or nested function, but can if you wrap in a function value
      • “sum _” is a partially applied func
      • sum(1, _: Int, 3) would return new function that takes 1 parameter
      • if leaving off all params can exclude underscore, only works where a function is required e.g. after foreach
    • closures
      • closures see variables not values they refer to so if variable changes, closures will see that
      • closure will access variable that was active at the time
    • special function call forms
      • args: String* acts as Array[String]
      • if passing in Array[String] need to pass in arr: _* each as own arg
      • named arg allow different order
      • can see default values in func parameters helpful when used with named parameters
    • tail recursion
      • recursive function that call themselves as last thing
      • compiler replaces it with jump to beginning of function
      • no runtime overhead
      • can only work for same function recursive call
      • also doesn’t if final val goes to function value
  9. Control Abstractions
    • higher order functions take in func as args
      • reduce duplication
      • closure / scope removes need to pass variables around
    • simplifying client code
      • collection.exists(x > 0) acts as a control abstraction
    • currying
      • curriedSum(x:Int)(y:Int) = x + y
      • onePlus = curriedSum(1)_ // placeholder notation
    • new control structure
      • new control structures by creating methods that take funcs as args
      • example is loan pattern / func has try finally that closes resource
      • can use curly braces instead of parens (one arg only)
      • enables writing function literals b/w curly braces
      • feels more like a control abstraction
    • By-name parameters
      • start with => instead of () =>
      • the evaluation occurs immediately if passing in just the type
      • boolAssert(predicate: Boolean)
      • byNameAssert(predicate: =>Boolean)
      • eval later in call
  10. Composition and Inheritance
    • a 2D layout library
      • will use as a running example
      • combinators combine elements into new ones
      • good way to think about library design
    • abstract Class
      • can have abstract methods and cannot be instantiated directly
      • method is abstract if not implementation
      • declare vs define
    • parameterless methods
      • “def height” vs “def height()”
      • uniform access principle, treat same if implemented as field or method
      • can leave off empty parens in scala
      • recommended to include when method represents more than a property of its receiver object (side-effects)
      • acts as cue that some interesting computation is taking place
    • extending classes
      • inherits all non-private members
      • makes subtype of parent
      • default extends scala.AnyRef (java.lang.Object)
      • overrided / implemented classes not inherited
      • value of subtype can be used where superclass is required
    • overriding methods and fields
      • can override w/ methods or field (val vs def)
      • field and methods cannot have same name
      • scala namespace: class and trait names
      • java namespace: fields, methods, packages, singleton objects
    • defining parametric fields
      • passing in “conts” to populate “contents” is code smell
      • avoid repetition  by combining parameter and field class “class ArrayElement(val contents: Array[String])”
      • other modifiers for class parameters: private, protected, override
    • invoking super-class constructors
      • invoke superclass constructor in extend
      • “class LineElement(s:String) extends ArrayElement(Array(s))”
    • override modifiers
      • requires “override” for concrete implementation
      • if you add methods to base class, risk breaking client code
      • in scala, if new base method is added that exists in client code, compiler error “needs override
    • polymorphism and dynamic binding
      • var of type Element can refer to ArrayElement
      • method invocations dynamically bound by class not type
      • “(e: Element) { .e.demo() }” calls base class if not implemented or overridden concrete method if it is
    • final
      • prevents method from being overridden
      • can also have final classes
    • composition and inheritance
      • generally prefer composition
      • ask if “is-a” relationship
    • implementing above, beside, and toString
      • using loops and indexes indicates imperative style
      • zip allows looping over corresponding pairs
    • defining a factory object
      • can hide hierarchy behind a factory object
      • factory object handles object creation
      • one simple approach is companion object
      • let subclasses be private so don’t have to be accessed directly
      • can define private class inside factory
  11. Scala’s Hierarchy
    • class hierarchy
      • every class inherits from Any and it’s methods
      • (==, !=, equals, ##, hashCode, toString)
      • any two subclasses: AnyVal and AnyRef
      • AnyVal parent of value classes: Byte, Short, Char, Int, Long, Float, Double, Boolean, Unit; which correspond to Java primitve types
      • implicits can convert these value types to another
      • RichInt provides methods such as min, max, until, to, abs
      • “booster” classes exist for other types
      • AnyRef base class for all reference classes
      • Recommended to use AnyRef everywhere (over “Object”)
      • scala classes differ from java classes in that they also inherit special marker trait “ScalaObject”
    • how are primitives implemented
      • in java difference between ref types and value types
      • Int x, Int y / x==y
      • Integer x, Integer y / x != y
      • in scala, “==” originally designed as reference equality but overridden in many subclasses to implement natural notation of equality
      • if you need reference equality use “eq”
    • bottom types
      • scala.Null and scala.Nothing
      • Null is not compatible with value types
      • Nothing has no values but subtype of everything so can in flexible ways e.g. throwing errors conforms to all return types
  12. Traits
    • how they work
      • can be mixed into classes
      • “mix in” not inherited
      • trait also defines a type
      • like java interfaces with concrete implementations
      • can also declare fields and maintain state
      • trait cannot take in class parameters
      • super call is statically bound in class and dynamically bound in trait
    • thin vs rich interfaces
      • thin less methods easier for implementers
      • rich: more potentially easier for clients calling to get what they need
      • define a trait with abstract methods for the thin part of the interface
      • the concrete methods (rich) can be implemented in terms of the thin methods
    • rectangular objects
      • can extend in abstract class or directly mix in
    • the ordered trait
      • the interface might just give “<“
      • rich may give you “<=”
      • Ordered trait defines >,<,<=,>= based on one “compare” method
    • traits as stackable modification
      • if trait is mixed in after a concrete implementation can stack
      • abstract override def put(x:Int) { .super.put(2 * x) }
      • order of mixins is significant
      • traits furthest to right take effect first
    • why not multiple inheritance
      • difference from multiple inheritance is interpretation of super
      • in multiple inheritance how to know which super to call? if you could call both, base method still called twice
    • to trait or not to trait (for reusable collection of behavior)
      • if behavior won’t be reused, make it a concrete class
      • if may be used in unrelated classes make a trait
      • if you want to inherit from it in java, use abstract class
      • if you expect to distribute it and expect outside groups to write classes inheriting from it go abstract class
      • for efficiency use class / traits get compiled as interfaces
      • if not sure start as a trait and change faster
  13. Packages and Imports
    • Putting code into packages
      • can put “package bobrockets.navigation”
      • and either on new line or a {} define class
    • concise access to related code
      • class can be accessed from own package
      • package can be accessed from its containing package
      • in curly braces, all names accessible in scope outside package also available inside it
      • scala provides _root_ outside any package a user can write
    • imports
      • can access with name instead of fully qualified name if imported
      • scala imports can appear anywhere
      • can import regular members
      • can also rename or hide members
    • implicit imports
      • automatically imports java.lang._, scala._, Predef._
    • access modifiers
      • private members visible inside class only
      • protected members accessible by subclasses
      • public: rest
      • qualified access gives fine-grained control over visibility, private[launch]
      • good for large projects where you can define things that are visible in several sub-packages but hidden from clients external to your project
      • class shares all access rights with companion object
    • package objects
      • can put helper methods at top level with package object
      • often use for package-wide type aliases and implicit conversions
      • usually in file bobsdelights/package.scala for “package object bobsdelights {}”
  14. Assertions and Unit Testing
    • assertions
      • assert or use ensuring block
      • can be enabled or disabled using JVM command line flags
    • unit testing
      • scalatest is example of tool
      • has execute method which is overridden by suite subtypes
    • informative failure reports
      • scalatest’s “===”, expect and intercept methods help write tests that are clear and concise
    • junit and testNG
      • Junit most popular on java framework
      • have scala wrappers
    • tests as specifications
      • spec, wordSpec, flatSpec, featureSpec, supports BDD
      • flatSpec you specify a subject and describe specific behaviors e.g. it should have a height equal to the passed in value
    • property-based testing
      • scalacheck lets you specify properties that code under test must obey
      • generates test data and see if property holds
  15. Case Classes and Pattern Matching
    • A simple example
      • case modifier adds some syntactic conveniences to your class
      • adds factory method Var(“x”) instead of new Var(“x”)
      • all parameters get val prefix
      • adds toString, hashCode, equals, equal checks structure recursively
      • adds a copy method which lets you pass in new parameters
      • cons: makes classes a bit bigger
      • pros: supports pattern matching
      • pattern matching starts with match and has pattern alternatives
      • constructor pattern: UnOp(“-“, e)
      • pattern matching is more generalized switch
      • Differences: A) match is an expression and returns a value B) alternative expressions never fall through to next case C) MatchError if not match
    • Kinds of Patterns
      • wildcard: “_” match all or parts you don’t care about BinOp(_,_,_)
      • constant: match only itself can be use literal as constant
      • variable: lets you keep operating on matched. scala uses lowercase var as pattern var. If you want to use variable use backtick. e.g. case `a`
      •  constructor: looks like BinOp(“+”, e, Number(0)). supports deep matches. can check arbitrary deep objects.
      • Sequence: List(0, _, _). List(0, _*) w/o specifying how long.
      • Tuple: case (a, b, c)
      • Typed patterns: case s: String, could also use expr.isInstanceOf[String], but more verbose and not recommended
      • Type erasure. no info about type arguments is maintained at runtime except fo arrays
      • variable binding can add var to any other pattern with @ sign e.g. UnOp(“abs”, e @ UnOp(“abs”, _)) => e
    • Pattern Guards
      • can’t assign same var, but can add constraints e.g. case BinOp(“+”, x, y) if x == y
    • Pattern overlaps
      • make sure to run more specific patterns before general catch-all patterns else unreachable code
    • Sealed Classes
      • can only have subclasses that extend the sealed abstract class in the same file
      • guarantees that pattern matching can be exhaustive
      • if you know you don’t need exhaustive check can add (e: @unchecked) match annotation
    • The Option Type
      • Some(x) or None
      • most common way to take apart is with pattern matching
      • less error prone than null in java
      • options specify which vals may be empty
    • Patterns Everywhere
      • val (number, string) = (123, “hi”) var def
      • useful for case classes e.g. val BinOp(op, left, right) = exp
      • case sequences as partial functions. case seq is more general function literal can have multiple entry points
      • generally use complete functions over partial functions because can have run-time errors that compiler can’t help with
      • sequence of cases defines a partial function
      • can also use in for expression and non matching values are discarded
  16. Working with Lists
    • list literals
      • immutable and elements cannot be changed by assignment
      • have recursive structure
    • list type
      • homogenous List[A]
      • covariant if A is subtype of B e.g. List[A] subtype of List[B]
      • List[Nothing] is subtype of all
    • constructing lists
      • made of Nil and :: (cons)
      • (“a” :: (“b” :: Nil)) or “a” :: “b” :: Nil
    • Basic operations on lists
      • head, tail, isEmpty
      • head/tail only for non-empty
    • List Patterns
      • can pattern match exact or parts
      • val List(a,b,c) = fruits
      • a :: rest
      • case x :: xs => insert(x, sort(xs))
      • pattern matching on lists is cleaner than decomposing with methods
    • first-order methods on class List
      • ::: concats two lists
      • most pattern match distinguishes empty list from non-empty
      • length is expensive operation better to use isEmpty
      • last and init opposite of head and tail but traverse whole lists
      • reverse creates new list
      • take, drop, splitAt takes an index
      • apply gets index item, but not common
      • flatten a list of lists
      • zip and unzip works on first matching n
      • mkString(prefix, sep, suffix), toString
      • iterator, toArray, copyToArray
    • Higher order methods on class List
      • map, flatMap, foreach, transform
      • applies function on each
      • filtering: filter, partition, find, takeWhile, dropWhile, span
      • you can use predicates over forall, exists
      • (1 /:)(_ + _) fold left
      • use :\ for fold right
    • methods of the list object
      • Apply List(1,2,3)
      • List.range(1,4)
      • List.fill(5)(‘a’)
      • List.tablulate / compute from a function
      • List.concat / combines list
  17. Collections
    • Sequences / Order
      • Lists – linked lists nice pattern matching
      • Arrays – random access use java arrays
      • List Buffers – mutable. constant time append/prepend. avoid stack overflow. avoid recursion.
      • Array Buffers – can add/remove from beginning and end. occasionally increase size, but avg time O(1)
      • Strings: can treat any string like a sequence. implicit conversion between string to stringOps
    • Sets and Maps
      • mutable and immutable versions
      • immutable sets with more than 5 elements use hash tries
      • Ordered sets/maps: implemented by red-black trees
    • Selecting mutable vs immutable collections
      • start with immutable first if not sure
      • if you find yourself worrying about making copies consider immutable
      • mutable could potentially save space and add performance if small
      • += can use with immutable and var
      • to change just import mutable one
    • initializing collections
      • literals. List(1,2). Map(1 -> 2)
      • toList, toArray, can be slow if data large
    • Tuples
      • saves you from having to define classes for simple classes
      • val (word, idx) different from val word, idx
      • if combination of data has some meaning good to create a class
  18. Stateful Objects
    • what makes and object stateful
      • purely functional objects always returns same things
      • stateful object returns things based on previous operations and some methods potentially returns different results
      • can have var and still be functional e.g. Cache
    • Reassignable variables and properties
      • can define getter and setter directly
      • var hour = 12 same as
      • def hour: Int = h
      • def hour_=(x:Int) { h = x }
      • can have control e.g. adding “require”
      • other options: log, notify subscribers
      • var celsius: Float = _ / “_” assigns types zero val
      • can have properties that don’t actually store its val e.g. Fahrenheit stores to celsius
    • case study: discrete event simulation
      • learn how to embed DSL into scala
      • simple framework for discrete simulative program
      • how to build and structure above
  19. Type Parameterization
    • Functional Queues
      • data structure with head, tail, enqueue
      • functional queue returns new queue
      • option 1) use list to implement, but problem is append takes linear time
      • option 2) represent a queue with two lists
    • Information Hiding
      • queue implementation constructor exposes two lists of which one is reversed
      • not intuitive so need to hide this constructor from client code
      • private constructors: class Queue[T] private(…)
      • constructor is private and can only be accessed within class
      • can add auxiliary constructor def this() = this(Nil, Nil)
      • can also add a factory method in companion object and still can Queue(1,2,3)
      • private class: hid class itself and only export trait. Queue[T] trait only exposes interface. type abstraction.
    • Variance annotations
      • Queue[T] cannot be instantiated. need to specify parameterized types. e.g. Queue[String]. Queue is a type constructor.
      • sub type questions. should Queue[String] be subtype of Queue[AnyRef]?
      • if yes, it is covariant.
      • In scala, generic types by default have “nonvariant” or rigid subtyping. Queue[String] cannot be used for Queue[AnyRef]
      • make covariant with trait Queue[+T]
      • [-T] is contravariant.
      • covariant, contravarient, non-variant are parameter variance
      • scala treats arrays as nonvariant. in java, they are covariant.
    • checking variance annotations
      • variance can be unsound even with no reassignable field
    • lower bounds
      • Queue[T] cannot be made covariant in T because T appears as a type parameter of enqueue method
      • unstuck by generalizing enqueue and make it polymorphic
      • def enqueue [U >: T](x: U) = new Queue[U]
      • “U >: T” T is lower bound for U.
      • U needs to be a super type
      • Queue[Apple] append orange returns Queue[Fruit]
    • contravariance
      • general principle in type driven design: safe to assume type T is a subtype of type U if you can substitute type T wher ea value of type U is required
    • object private data
      • object private members can be accessed from within object they are defined and do not cause problems with variance
    • upper bounds
      • to require type passed in mixes in trait use upper bound “<:”
  20. Abstract Members
    • Quick Tour
      • type, method, val, var
      • concrete needs to implement all
    • Type members
      • alias type
      • use when real name is verbose or less obvious in meaning
    • abstract vals
      • val initial: String
      • always return same thing
      • def cannot override val
    • abstract vars
      • expands into getter and setter defs
    • abstract vals
      • lets you provide details in subclass
      • can define anonymous class with trait “new Trait {}”
      • may have some issues because of timing of initialization
      • pre-initialized fields: initialize and then extend “new {} with RationalTrait”
      • “object something extends {} with RationalTrait”
      • lazy vals: if you prefix lazy, only evaluated when used even once. order doesn’t matter if no side effects.
    • abstract types
      • type currently unknown
      • type SuitableFood <: Food
      • type SuitableFood = Grass
    • Path Dependent Types
      • based on object
      • class Outer { class Inner } Outer#Inner
      • “.” reserved for objects
      • val o1 = new Outer
      • new o1.Inner works because references specific class of instance object
      • Outer#Inner does not name a specific instance so doesn’t work.
    • Structural subtyping
      • direct subtyping in nominal subtyping
      • structural: two types have same members. refinement types.
      • usually more flexible than you want.
      • Animal { type SuitableFood = Grass }
      • also useful if group classes written by others. e.g. loan pattern with close method.
    • Enumerations
      • define objects that extends Enumeration
      • Color.Red is Color.Value type which is different from Direction.Value. path dependent.
  21. Implicit Conversions and Parameters
    • Intro
      • implicits are about extending or working with someone else’s library
      • leave out details that obscure interesting parts of code
    • implicit conversions
      • usually help two bodies of software developed without each other in mind
      • each may encode same concept in own way
      • reduce explicit conversions from 1 type to another
      • in java, can’t pass function literal to even listener so define class with method
      • in scala, may wan to just pass a function, but it expects a method
      • you can define an implicit conversion
      • can call directly else compiler will insert
      • compiler first tries to compile, then looks for implicit that makes this work
    • rules for implicits:
      • marking rule: only definitions marked implicit are available
      • scope rule: must be in scope and available as a single identifier. not “someVariable.convert” can add in companion object
      • one-at-a-time rule: can’t try an implicit on implicit
      • first-rule: if code type checks no implicits are attempted
      • names can be arbitrary might be good if importing only one or a specific conversion
    • implicit conversion to an expected type
      • first place compiler will use
      • if I see X, but read Y, look for X to Y implicit
      • compiler inserts the function
      • usually move to a more constrained type to more general is better e.g. Int -> Double, not other way
    • converting the receiver
      • receiver of method call / object on which method invoked
      • 1 + Rational. Int does not have method that takes a Rational, compiler looks for conversion of Int with “+” method that can take a Rational and converts the Int to Rational
      • “->” is a method. 1 -> “one” converts 1 to ArrowAssoc. which returns tuple2
      • “rich wrappers” pattern is common in libraries that provide syntax-like extensions to the language
      • if you see someone calling methods not in receiver class, prob implicit
    • implicit parameters
      • add missing parameter to complete function call
      • to take param implicitly method must have implicit (implicit prompt: PreferredPrompt)
      • the val being passed in must also be marked implicit
      • implicit keyword applies for entire param list not single param
      • implicit params usually have “rare” or “special” types that accidental matches are unlikely won’t be in scope unless intended
      • good to use implicits on very specific classes
    • view bounds
      • since may not need to use name can convert
      • def maxList[T](elements: List[T])(implicit order: T => Ordered[T]): T
      • to
      • def maxList[T <% Ordered[T]](elements: List[T]):T
      • “T <% Ordered[T]” means can use any T as long as it can be treated as an Ordered[T]
    • when multiple conversions apply
      • scala won’t insert if multiple apply
      • implicits are good if conversion is boilerplate and obvious
      • if multiple apply, non-obvious
      • scala will choose one if its more specific
      • 1) the arg type of former is subtype of latter
      • 2) both conversions are methods and enclosing class of former extends the enclosing class of latter
    • Debugging
      • maybe has wrong return type
      • maybe not in scope try calling explicitly
      • “scalac -Xprint:typer filename.scala” can show implicit conversions
  22. Implementing Lists
    • List class in principle
      • not built-in language construct
      • “::” and “Nil” are subclasses
      • abstract class List[+T]
      • isEmpty, head, tail methods define it
      • Nil empty list inherits from List[Nothing] subtype of all lists
      • “::” or cons find case class ::[T](head:T, tail:List[T]) extends List[T] {…}
      • ::, ::: bound to right operand
      • cons doesn’t necessarily return same type
      • can return more general type orange :: apples return List[Fruit]
    • The ListBuffer class
      • access pattern typically recursive
      • match case List() => .. case x :: xs => …
      • would be very expensive if not tail recursive
      • can use List Buffer to append and toList in constant time
    • List Class in Practice
      • generally use loops with list buffers
      • appending actually modifies tail
    • functional on the outside
      • why purity, why not make mutable?
      • a lot of sharing, would make fragile and unpredictable
      • val ys = 1 :: xs
      • val zx = 2 :: xs
  23. For Expressions Revisited
    • For expressions
      • for (seq) yield expr
      • seq is sequence of generators, definitions and filters
      • if no match item is discarded
    • n-queens: good for combinatorial puzzles
    • querying with for expressions
      • similar to db querying
    • translation
      • expressed in map, flatMap, withFilter
      • multiple generators are converted to flatMap calls
      • patterns are matched with partial case functions
      • only those matching a pattern will pass filter so never see MatchError
      • should only declare embedded definitions that use iterated generator else declare val outside
    • Going the other way
      • easily implement map, flatMap, filter with for loops
    • generalizing for
      • can use on arbitrary classes that implement map, flatMap, foreach, withFilter
      • represents methods of monads and how to interact with the type
  24. The Scala Collections API
    • Mutable and Immutable Collections
      • “scala.collection.immutable” guaranteed to never change
      • “scala.collection.mutable” can be changed
      • “scala.collection” can be either, generally root collections define an interface
      • also “collection.generic” has building blocks for implementing collections, but most users shouldn’t need to use
    • consistency
      • all can be created with a class e.g. Iterable(1,2,3)
      • also for specific implementation e.g. List(1,2,3)
    • trait traversable
      • top of hierarchy
      • only abstract operation is “foreach”
      • implements many other operations: addition, map, conversions, copying, size, elem retrieveal, subcollection retrieval (tail), subdivision (splitAt), element tests (exists), folds, stringOps (mkString), view operations
    • Trait iterable
      • abstract method iterator
      • also has “grouped” and “sliding”
      • also gives takeRight, dropRight, zip, zipAll, zipWithIndex
      • why traversable and iterable? can be ore efficient to implement foreach. iterator concat, could increase runtime e.g. tree traversal from 2N to NlogN
      • 3 subcategories in iterable: Seq, Set, Map
    • Sequence traits: Seq, IndexedSeq, LinearSeq
      • index and length ops (apply, length, indicies)
      • index search ops (indexOf, etc)
      • Addition Ops: (+:, :+) append, prepend
      • update ops: updated, patch
      • sorting
      • reversal
      • comparison
      • multiset (intersection, diff, union)
      • two subtraits: LinearSeq and IndexedSeq diff performance characteristics
      • linear good for head, tail ops and indexed good for apply/length ops
      • vectors provide effective constant time indexing overhead and constant time linear access overhead
      • buffers: mutable sequences. efficient insert and removals. 2 common implementations. ListBuffer and ArrayBuffer. convert to array or list efficiently.
    • Sets
      • +, ++, -, –, not often used for mutable sets because requires copying
      • mutable sets implemented with hash table and immutable sets implemented by hash tries
      • sortedset: elems traversed in sorted order uses red/black tree to keep order and balance. supports range or from.
      • bit sets: one or more non-neg int elems use array of longs. good if many small elements.
    • Maps
      • “a” -> 1 converts to (“a”,1)
    • Synchronized sets and maps
      • if you need thread-safe map, can mix in Synchronized Map trait to your map
      • extending generates synthetic subclass of HashMap
      • new map may be accessed by multiple threads at once
      • if using synchronized collections may consider concurrent collections instead or actors
    • concrete immutable collection classes
      • scala provides many
      • Lists: finite constant time access to head/tail. constant cons. many other ops. linear.
      • Streams: elems computed lazily can be infinitely long only requires elements computed. similar performance as lists.
      • Vectors: efficient access beyond head. any part. represented as broad shallow trees. 32 elements per node effectively constant time for most reasonably sized collections. default for IndexedSeq.
      • immutable stacks: rarely used. functionality presumed by lists.
      • immutable queues: FIFO
      • Ranges: represented in constant space because represented by start, end, increment
      • Hash tries: 32 elem nodes. selection by hash code.
      • red black tries: balanced binary tree.
      • immutable bit sets: array of longs. very fast lookup.
      • list maps: may need to iterate everything. not really used.
    • concrete mutable collection classes
      • array buffer: good if adding items to end. amortized constant time.
      • list buffers: use linked list internally. constant append/prepend. better if converting to list later.
      • linked lists: makes easier to insert an element or another linked list
      • double linked list: also has “prev” makes removal fast
      • mutable lists: pointer to terminal empty node. fast append. std implementation of mutable.LinearSeq
      • queues: instead of enqueue use “+=”, “++=”
      • array sequences: fixed size for performance reasons. but want generic instances where do not know type of elements. Array[AnyRef]
      • array stacks: fast indexing and gets resized s read. usually more efficient that normal mutable stack.
      • hash tables: underlying arrays. if need ordered use linked hash map.
      • weak hash maps: key, val is GC’d if no other reference to key
      • mutable bit sets: slightly more efficient because no copying.
    • arrays
      • correspond one-to-one to java arrays
      • scala arrays can be generic
      • compatible with Seq and support all Seq operations (map, filter)
      • use implicit conversions from arrays and instances of class mutable.WrappedArray (subclass of Seq)
      • also implicit to ArrayOps which doesn’t convert but provides sequence methods and returns array
      • how does compiler know which conversion to prioritize? because WrappedArray and array ops both support e.g. reverse
      • implicits in subclasses and subobjects take precedence
      • Array[T] is mapped to array of java.lang.Object
      • can’t just declare abstract types because actual type that corresponds to type parameter is erased at run-time
      • need to hint with scala. reflect. ClassManifest
      • define as implicit param.
    • Strings
      • two implicit conversions 1) low priority. WrappedString (subclass of immutable.IndexedSeq) 2) high priority StringOps
    • Equality
      • different collections are always unequal
      • equal only if same elements
    • Views
      • map, filter, called transformers
      • take on collection and return another
      • strict and lazy implementations
      • lazy computes on demand i.e. with iterator
      • default behavior is strict. except stream
      • view special collection that implements all lazily.
      • “view” to make lazy and “force” to convert
      • v.view map (_ + 1) map (_* 2).force
      • makes intermediate data structure unnecessary
      • scala provides for general collection types not specific implmentation. knowledge of vector lost get Seq.
      • helps ensure performance and modularity
      • use in scenarios
        • 1) fp way where transformers have no side effects
        • 2) apply over mutable collection where all modifications done explicitly
      • not good to have side effects and return collections
    • Iterators
      • not collection. way to access elem one by one
      • iterator can’t iterate once you’ve stepped through it
      • duplicate method lets you iterate twice
    • Collections from scratch
      • use collection name and apply on companion object
    • Java <-> Scala
      • collections JavaConversions._ sets implicits between java and scala counterpars
  25. The Architecture of Scala Collections
    • Builders
      • building new collections is handled by instances of class Builder
      • builders maintain state. you can add clear, get result
      • generic for elems and what it returns for results
      • often refer to some other builder for assembling elements and transform results to another type
      • can use mapResult to return new type
      • val buf = new ArrayBuffer[Int]
      • val bldr = buf mapResult(_.toArray)
    • factoring out common operations
      • main objective is to have natural types and maximal sharing
      • transforamtions on collections should yield same types e.g. map/filter List to List
  26. Extractors
    • An example: extracting email addresses
      • if else clunky esp if trying to do multiple
      • what if “s match { .case Email(user, domain) }” could work?
    • extractors
      • object that has unapply and optionally apply
      • def unapply(str: String): Option[(String,String)] = {val parts = str split “@”; if parts.length == 2 Some(parts(0), parts(1)) else None}
      • unapply can also work on more general types
      • good to have symmetry in apply and unapply
    • patterns with zero or one variables
      • there is no one element tuple in scala, just return optional
      • can also return non, just true or false
    • variable argument extractors
      • “unapplySeq” for multiple args
    • extractors and sequence patterns
      • List(), List(x, y, _*) works because companion object defines unapply
    • Extractors versus case classes
      • short coming of case class: expose concrete repr of data
      • if match, you know its an instance
      • extractors have representation independence
      • if you used case classes, you’d be stuck with them in client code because pattern match to them
      • pros: 1) case classes easier to set up 2) more efficient 3) if sealed, compiler matches for exhaustiveness
      • if closed application, prefer case classes and can refactor
      • if expose types to unknown client prefer extractors
    • Regex
      • extractors play nicely with regex
      • val Decimal = new Regex(“””(-)?(\d)(\.\d*)?”””)
      • val Decimal(sign, integerPart, decimal) = “-1.23)
      • corresponds to groups. set to null if not matching part.
  27. Annotations
    • why have annotations?
      • automatic generation of documentation as with scaladoc
      • pretty printing code so matches you preferred style
      • checking code for common errors
    • syntax
      • e.g. “@deprecated def bigMistake()”
      • annotations allowed in any kind of declaration or definition
      • can use an expression e.g. pattern matching
      • can also take args
    • standard annotations
      • deprecation
      • volatile fields: make reads/writes slower for var accessed by multiple threads. more predicatable
      • binary serialization: obj <-> bytes. for pickling. default classes not considered serializable. add “@serializable” if you would like to be. add serial no. @serialVersionUID(123). “@transient” not same field. will restore default when loaded.
      • get/set: don’t need in scala, but some platform-specific frameworks might expect
      • tailrec: tail recursive
      • unchecked: for pattern matching. tells compiler not to worry if match expression seems to lave out some cases.
      • native: inform compiler method implementation supplied by run-time. up to developer to supply implementation using mechanism such as JNI.
  28. Working with XML
    • Semi-structured
      • organized into tree no types
      • good for serialization
      • most widely used format on internet
      • likely to encouter
    • XML overview
      • text and <tags>
      • must have open and closed
      • tags can have attributes
    • XML literals
      • XML element can write a literal
      • can insert scala code with {}
    • serialization
      • add toXML method to data-heavy classes
    • taking apart
      • call text method on any XML node get text decoded automatically
      • subelems with (\”b”) or \\ “b” for deep
      • \”@name” for extracting attributes
    • deserialization
      • parser def fromXML(node: scala.xml.Node)
      • instantiate class with extracting fields and vals
    • loading and saving
      • conversion between XML and stream of bytes
      • “XML.save” specify filename then can load “XML.loadfile(filename)”
    • pattern matching on XML
      • can match exact tags or seq
      • case <a>{contents @_*}</a>
      • white space counts as nodes
      • for loop and matching
  29. Modular Programming Using Objects
    • The problem
      • as programs grow, modularity important
      • teams can work independently and can use / switch out modules, diff configs for different contexts
      • unit, integration, deployment contexts
      • first need separation between interface and implementation
      • second, way to replace module with same interface without changing or recompiling modules
      • java uses dependency injection which can also be used in scala
    • a recipe application
      • scala uses objects for modules
      • can define mock objects
    • abstraction
      • hard link between db module and browser module in example
      • no way to use different implementation of browser
      • make more pluggable but avoid duplicate code
      • use abstract class to define common parts
      • abstract browser refers to abstract database
    • splitting modules into traits
      • define as traits and mixins
      • may have compile problems if certain objects out of scope if trait always used with another trait can specify that those traits should be assumed
      • “Self type” is assumed type for “this”. specifies requirements on any concrete class trait is mixed into
    • runtime linking
      • can set at runtime. val db: Database = studentDb if “student” else ..
      • will always need to specify actual implementation somewhere
      • in spring, external XML file. compiler, spellcheck @ least in scala code
    • Tracking module instances
      • sometimes compiler doesn’t know two types are the same
      • you can specify with singleton types “val database: db.type = db”
      • singleton type very specific and refers to only one object can help compiler
  30. Object Equality
    • Java
      • has object and value equality methods
      • “==” is natural equality for value types abut object equality for reference types
      • in scala “==” is natural equality. “eq” used for obejct equality, but not used much
      • can override “==” by overriding “equals” which by default is “eq”
    • writing an equality method
      • surprisingly difficult
      • defining equals with wrong signature root uses “def equals(other: Any): Boolean”
      • change equals without changing hashCode. existing version uses address of allocated object
      • defining equals in terms of mutable fields.
      • failing to define equals as an equivalence relation
        • should be for non-null objects
        • may not be symmetric if dealing with subclasses
        • could make equals more general, but then can violate transitive property e.g. blue == p. red == p. blue != red
        • can allow only match on same class, but what about anonymous subclasses? effectively same classes?
        • canEqual can specify if class needs to match
        • programmers who implement subclasses decide if may be equal to superclass
    • equality for parameterized types
      • can’t match Branch[T] for types because type erasure
      • use either Branch[t] or  Branch[_] to represent unknown types
    • equals and hashCode
  31. Combining Scala and Java
    • Using scala from java
      • tries to map to Java types
      • traits have no equivalent unless all abstract members
      • singleton objects represented by class
    • annotations
      • deprecation, volatile, serialization
      • can add annotations with @throws exception to satisfy java requirement
    • existential types
      • for wildcard and raw types
      • usually used when accessing Java types from scala
      • Iterator[T] forSome { type T} or Iterator[T]
      • also add upper and lower bound Iterator[_ <: Component]
      • to use, usually read object with abstract members just use that to begin with
    • using synchronized
      • scala includes a predefined synchronized method
      • allows one thread in at a time
    • compiling scala and java together
      • usually if scala code depends on java code, first build java to class files, then build scala code putting java code in class path
      • doesn’t work if java references scala
      • can compile against java source to get around
  32. Actors and Concurrency
    • trouble in paradise
      • multithreading in java can be hard to get right
      • can be hard to test and get right via reason alone
      • scala provides share-nothing approach
    • actors and message passing
      • thread-like entity
      • extends Actor and implements act
      • invoke with “start”
      • can create with utility method actor {…}
      • send message with “!”
      • actor can receive message and match with partial function
      • ignore message types that don’t match
    • treating native threads as actors
      • don’t need to think about how threads map to actor
      • can use native thread as an actor
      • can use Actor.self can be for debugging
      • use current thread as actors
        • import scala.actors.Actor._
        • self ! “hello”
        • self.receive { case x => x }
      • better to use “receiveWithin” to specify timeout
    • Better performance through thread resuse
      • each actor must be given own thread
      • threads not cheap
      • usually JVM can have thousands and switching threads often take hundreds-thousands of processor cycles
      • can use “receive” instead of “react” which result type Nothing doesn’t preserve call stack / more performant
      • try to use react when possible
    • Good actors style
      • should not block
      • block on helper actor
      • communicate only with messages or can pass synchronized collection
      • prefer immutable messages (e.g. case classes)
      • make message self-contained
      • calling actor should not block
      • how to know what caller was doing?
      • make it return redundant/additional info
      • could also return specific case class making it easier to find possible responders in code
Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s