- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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 {}”
- 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
- 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
- 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
- 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
- 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
- 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 “<:”
- 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.
- 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
- 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
- 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
- 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
- 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
- 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
- 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.
- 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.
- 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
- 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
- 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
- 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
- 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
Like this:
Like Loading...