Scala is a modern multi-paradigm programming language designed to express common programming patterns in a concise, elegant, and type-safe way. It smoothly integrates features of object-oriented and functional languages.
Most given examples require a working Scala installation. This is the Scala installation page, and this is the 'How to setup Scala' example. scalafiddle.net is a good resource for executing small code examples over the web.
Scala has first-class functions.
A function is not a method in Scala: functions are a value, and may be assigned as such. Methods (created using def
), on the other hand, must belong to a class, trait or object.
Function1
) at compile-time, and are instantiated to a value at runtime. Methods, on the other hand, are members of their class, trait or object, and do not exist outside of that.The primary difference between while
and do-while
loops is whether they execute the block_expression
before they check to see if they should loop.
Because while
and do-while
loops rely on an expression to evaluate to false
to terminate, they often require mutable state to be declared outside the loop and then modified inside the loop.
Approach with sealed trait
and case objects
is preferred because Scala enumeration has a few problems:
scala.MatchError
:def isWeekendWithBug(day: WeekDays.Value): Boolean = day match {
case WeekDays.Sun | WeekDays.Sat => true
}
isWeekendWithBug(WeekDays.Fri)
scala.MatchError: Fri (of class scala.Enumeration$Val)
Compare with:
def isWeekendWithBug(day: WeekDay): Boolean = day match {
case WeekDay.Sun | WeekDay.Sat => true
}
Warning: match may not be exhaustive.
It would fail on the following inputs: Fri, Mon, Thu, Tue, Wed
def isWeekendWithBug(day: WeekDay): Boolean = day match {
^
More detailed explanation is presented in this article about Scala Enumeration.
This feature exists in Scala 2.10.0 and above.
Scala goes to great lengths to treat methods and functions as syntactically identical. But under the hood, they are distinct concepts.
A method is executable code, and has no value representation.
A function is an actual object instance of type Function1
(or a similar type of another arity). Its code is contained in its apply
method. Effectively, it simply acts as a value that can be passed around.
Incidentally, the ability to treat functions as values is exactly what is meant by a language having support for higher-order functions. Function instances are Scala's approach to implementing this feature.
An actual higher-order function is a function that either takes a function value as an argument or returns a function value. But in Scala, as all operations are methods, it's more general to think of methods that receive or return function parameters. So map
, as defined on Seq
might be thought of as a "higher-order function" due to its parameter being a function, but it is not literally a function; it is a method.
Implicit classes allow custom methods to be added to existing types, without having to modify their code, thereby enriching types without needing control of the code.
Using implicit types to enrich an existing class is often referred to as an 'enrich my library' pattern.
Restrictions on Implicit Classes
As val
are semantically static, they are initialized "in-place" wherever they appear in the code. This can produce surprising and undesirable behavior when used in abstract classes and traits.
For example, let's say we would like to make a trait called PlusOne
that defines an increment operation on a wrapped Int
. Since Int
s are immutable, the value plus one is known at initialization and will never be changed afterwards, so semantically it's a val
. However, defining it this way will produce an unexpected result.
trait PlusOne {
val i:Int
val incr = i + 1
}
class IntWrapper(val i: Int) extends PlusOne
No matter what value i
you construct IntWrapper
with, calling .incr
on the returned object will always return 1. This is because the val incr
is initialized in the trait, before the extending class, and at that time i
only has the default value of 0
. (In other conditions, it might be populated with Nil
, null
, or a similar default.)
The general rule, then, is to avoid using val
on any value that depends on an abstract field. Instead, use lazy val
, which does not evaluate until it is needed, or def
, which evaluates every time it is called. Note however that if the lazy val
is forced to evaluate by a val
before initialization completes, the same error will occur.
A fiddle (written in Scala-Js, but the same behavior applies) can be found here.
Single Abstract Methods are types, introduced in Java 8, that have exactly one abstract member.
Streams are lazily-evaluated, meaning they can be used to implement generators, which will provide or 'generate' a new item of the specified type on-demand, rather than before the fact. This ensures only the computations necessary are done.
ParseResult Cases
A ParseResult
comes in three flavors:
Scala-lang provides a list of standard annotations and their Java equivalents.
Macros are a language feature that need to be enabled, either by importing scala.language.macros
or with the compiler option -language:macros
. Only macro definitions require this; code that uses macros need not do it.
To avoid serialization problems, particularly in distributed environments (e.g. Apache Spark), it is a best practice to implement the Serializable
trait for type class instances.
Parallel collections facilitate parallel programming by hiding low-level parallelization details. This makes taking advantage of multi-core architectures easy. Examples of parallel collections include ParArray
, ParVector
, mutable.ParHashMap
, immutable.ParHashMap
, and ParRange
. A full list can be found in the documentation.
Prefer vals, immutable objects, and methods without side effects. Reach for them first. Use vars, mutable objects, and methods with side effects when you have a specific need and justification for them.
-- Programming in Scala, by Odersky, Spoon, and Venners
There are more example and guideline in this presentation by Odersky.
Often used with the cake pattern.
Why are tuples limited to length 23?
Tuples are rewritten as objects by the compiler. The compiler has access to Tuple1
through Tuple22
. This arbitrary limit was decided by language designers.
Why do tuple lengths count from 0?
A Tuple0
is equivalent to a Unit
.
It is recommended to use value classes for units or a dedicated library for them.
Constant names should be in upper camel case. That is, if the member is final, immutable and it belongs to a package object or an object, it may be considered a constant
Method, Value and variable names should be in lower camel case
Source: http://docs.scala-lang.org/style/naming-conventions.html
This compile:
val (a,b) = (1,2)
// a: Int = 1
// b: Int = 2
but this doesn't:
val (A,B) = (1,2)
// error: not found: value A
// error: not found: value B
Scala comes with a concept of symbols - strings that are interned, that is: two symbols with the same name (the same character sequence), in contrary to strings, will refer to the same object during execution.
Symbols are a feature of many languages: Lisp, Ruby and Erlang and more, however in Scala they are of relatively small use. Good feature to have nevertheless.
Use:
Any literal beginning with a single quote '
, followed by one or more digits, letters, or under‐scores _
is a symbol literal. The first character is an exception as it can’t be a digit.
Good definitions:
'ATM
'IPv4
'IPv6
'map_to_operations
'data_format_2006
// Using the `Symbol.apply` method
Symbol("hakuna matata")
Symbol("To be or not to be that is a question")
Bad definitions:
'8'th_division
'94_pattern
'bad-format
In order to declare subtypes of Dynamic
, the language feature dynamics
must be enabled, either by importing scala.language.dynamics
or by the -language:dynamics
compiler option. Users of this Dynamic
who do not define their own subtypes do not need to enable this.
shift
and reset
are primitive control flow structures, like Int.+
is a primitive operation and Long
is a primitive type. They are more primitive than either in that delimited continuations can actually be used to construct almost all control flow structures. They are not very useful "out-of-the-box", but they truly shine when they are used in libraries to create rich APIs.
Continuations and monads are also closely linked. Continuations can be made into the continuation monad, and monads are continuations because their flatMap
operation takes a continuation as parameter.