Scala Notes - fcrimins/fcrimins.github.io GitHub Wiki
Dependency Injection in Scala: MacWire (12/13/16)
- "Services need to expose information on what dependencies they need. Instead of creating dependent service implementations inside the service itself, references to dependent services are 'injected'."
- "The means of injecting the dependencies ... we will be using here is passing dependencies through constructor parameters."
- "The dependencies form an object graph, which needs to be wired."
- "Note that we can declare all abstract members as
def
s, as they can be later implemented asval
s,lazy val
s, or left asdef
s. Using adef
keeps all options possible." - "DI in Akka - Akka is a very popular toolkit for bulding concurrent, 'reactive' applications. The main building block used in Akka-based systems are actors. A common question when using Akka is "how do I do dependency injection with actors?"; typical use-case is passing a datasource to an actor."
- unions, like in C++ or like Either[A,B] in Scala (which is apparently considered "boxed"), not like tuples
- includes nice explanation of tail recursion (see gcd/factorial example, p. 18) (12/2/16 - 12/12/16)
- "A production quality Scala implementation is therefore only required to re-use the stack frame of a directly tail-recursive function whose last action is a call to itself."
- "Scala uses call-by-value (FWC - parameters are evaluated before calling) by default, but it switches to call-by-name (FWC - unevaluated references to parameters are passed and only evaluated upon use) evaluation if the parameter type is preceded by =>" (p. 14)
- Indeed, the anonymous function
(x1: T1, ..., xn: Tn) => E
is equivalent to the block{ def f (x1: T1, .., xn: Tn) = E ; f _ }
wheref
is fresh name which is used nowhere else in the program. We also say, anonymous functions are 'syntactic sugar'. - Currying (FWC - multiple parameter lists reduce to curried functions) "The style of function-returning functions is so useful that Scala has special syntax for it." (p. 24)
- "Generally, a curried function definition
def f (args_1) ... (args_n) = E
expands todef f (args_1) ... (args_{n-1}) = (args_n) => E
" - Also note, that functions that do not take any parameters are a special case of this.
- "Generally, a curried function definition
- Really neat example of fixed point functions: the square root function can be expressed as follows
def sqrt(x: Double) = fixedPoint(averageDamp(y => x/y))(1.0)
(p. 27) - "Note that, unlike in Java, redefining definitions need to be preceded by an override modifier." (p. 33) [FWC - this is something that a lot of OO languages are missing; it prevents bugs]
- "Dynamic method dispatch is analogous to higher-order function calls. In both cases, the identity of code to be executed is known only at run-time. This similarity is not just superficial. Indeed, Scala represents every function value as an object (see Section 8.6)." (p. 36)
- Injector/extractor - "Taking a closer look, one observes that the only purpose of the classification and access functions is to reverse the data construction process. They let us determine, first, which sub-class of an abstract base class was used and, second, what were the constructor arguments. Since this situation is quite common, Scala has a way to automate it with case classes." (p. 46)
- "This also affects the implementation of == and !=, which are implemented in terms of
equals
in Scala. So,Sum(Number(1), Number(2)) == Sum(Number(1), Number(2))
will yieldtrue
. IfSum
orNumber
were not case classes, the same expression would befalse
, since the standard implementation ofequals
in classAnyRef
always treats objects created by different constructor calls as being different." -
{ case P1 => E1 ... case Pn => En }
is shorthand for the following function(x => x match { case P1 => E1 ... case Pn => En })
- "This also affects the implementation of == and !=, which are implemented in terms of
- "The parameter declaration
A <: Ordered[A]
introducesA
as a type parameter which must be a subtype ofOrdered[A]
." (p. 54)- "One problem with type parameter bounds is that they require forethought: if we had not declared
Num
a subclass ofOrdered
, we would not have been able to useNum
elements in sets. By the same token, types inherited from Java, such asInt
,Double
, orString
are not subclasses ofOrdered
, so values of these types cannot be used as set elements. ... View bounds<%
are weaker than plain bounds<:
. A view bounded type parameter clause[A <% T]
only specifies that the bounded typeA
must be convertible to the bound typeT
, using an implicit conversion." (p. 55)
- "One problem with type parameter bounds is that they require forethought: if we had not declared
- "In Scala, generic types have by default non-variant subtyping", not co-variant subtyping: if
T
is a subtype ofS
, thenStack[T]
is a subtype ofStack[S]
(p. 56)- But co-variant is possible w/
+
:class Stack[+A] {
- "Scala uses a conservative approximation to verify soundness of variance annotations. A covariant type parameter of a class may only appear in co-variant positions inside the class. Among the co-variant positions are (1) the types of values in the class, (2) the result types of methods in the class, and (3) type arguments to other covariant types." The types of formal method parameters are not co-variant! (p. 57)
- "However, there are also methods which do not mutate state, but where a type parameter still appears contra-variantly. An example is
push
in typeStack
. ... there is a a way to solve the problem by using a polymorphic method with a lower type parameter bound." (p. 57)- "
T >: S
, the type parameterT
is restricted to range only over supertypes of typeS
" - "Using lower bounds, we can generalize the push method in
Stack
as follows."class Stack[+A] { def push[B >: A](x: B): Stack[B] = new NonEmptyStack(x,this)
- "Technically, this solves our variance problem since now the type parameter
A
appears no longer as a parameter type of methodpush
. - "Now, we can push also elements of a supertype of this type, but the type of the returned stack will change accordingly. For instance, we can now push an
AnyRef
onto a stack ofString
s, but the resulting stack will be a stack ofAnyRef
s instead of a stack ofString
s!"
- "
- But co-variant is possible w/
- See Type Tags section in particular
Scala Mixins (9/21/16)
class X extends B
class Y extends B
class D extends X with Y
- Only the members of Y that are explicitly defined inside Y are "mixed in" to D.
- D receives all of its B functionality via X.
- This prevents the diamond problem of multiple class inheritance.
Q: Why are the elements of Scala tuples indexed starting from 1 rather than 0?
A: Because they aren't indexes. They're elements in a basket--class members, fields--that just don't have names (yet). Thinking about them as indexes is the wrong way of thinking about them. So starting from 1 discourages this way of thinking. [FWC]
Scala Implicits (9/14/16)
Implicit Conversions and Parameters
- Implicit Receiver Conversion: "Implicit conversions also apply to the receiver of a method call, the object on which the method is invoked. This kind of implicit conversion has two main uses. First, receiver conversions allow smoother integration of a new class into an existing class hierarchy. And second, they support writing domain-specific languages (DSLs) within the language."
- This "rich wrappers" pattern is common in libraries that provide syntax-like extensions to the language, so you should be ready to recognize the pattern when you see it. Whenever you see someone calling methods that appear not to exist in the receiver class, they are probably using implicits. Similarly, if you see a class named RichSomething, e.g., RichInt or RichString, that class is likely adding syntax-like methods to type Something.
- As you can now see, these rich wrappers apply more widely, often letting you get by with an internal DSL defined as a library where programmers in other languages might feel the need to develop an external DSL.
- "more than one scala library found in the build path" - http://stackoverflow.com/questions/13682349/how-to-use-just-one-scala-library-for-maven-eclipse-scala
- "is cross-compiled with an incompatible version of Scala - http://scala-ide.org/docs/current-user-doc/faq/index.html
- "plugin execution not covered by lifecycle configuration" - https://www.eclipse.org/m2e/documentation/m2e-execution-not-covered.html
- this means that Eclipse's "lifecycle configuration" isn't going to run this Maven plugin because they aren't hooked up to each other
- "My observation so far about abstract type members is that they are primarily a better choice than generic type parameters when you want to let people mix in definitions of those types via traits. You may also want to consider using them when you think the explicit mention of the type member name when it is being defined will help code readability."
Scala on Wikipedia (6/7/16)
- "By convention, a method should be defined with empty-parens when it performs side effects."