● lecture 1: introduction to functional programming
functional programming
compiler, a translator that takes the code you write in a high-level programming language (like Python, Java,
or C++) and translates it into a lower-level language (usually machine code or bytecode) that the computer
can understand. this translation process happens before the program is run. so, when you compile your code,
you're essentially converting it into a form that the computer can directly execute.
– statically typed language, the data types of variables are determined at compile time (scala, java)
runtime, the environment where your compiled program runs. it includes everything needed to execute your
program, like libraries, utilities, and other resources. when you run your compiled program, the runtime
manages its execution, handling tasks like memory allocation, input/output operations, and error handling.
– dynamically typed language, the data types of variables are determined during runtime (python)
scala is a declarative style of programming in which functions map values to other values instead of a
sequence of imperative statements which alters the running state of the program
– declarative style, you declare what you want to happen, rather than specifying how to do it. and you
describe relationships between inputs and outputs using functions, rather than writing a sequence of
steps to change the program's state (imperative style of programming)
advantages of functional programming
– immutable data, once you define a variable, its value doesn't change. this makes code more predictable
and easier to reason about since you don't have to worry about unexpected changes.
– referential transparency, this means that an expression or function always gives the same output for the
same input. it enhances predictability and makes debugging easier.
– strong typing and polymorphism, functional languages typically enforce strong typing, which helps catch
errors at compile-time. polymorphism allows functions to work with different data types.
– recursion, instead of loops, functional programming emphasizes recursion for repetitive tasks.
– suited for parallel computing, since there are no issues with state changes, functional programming is
well-suited for parallel computing, where multiple computations can be performed simultaneously.
– lazy evaluation, values are computed only when needed, which can improve performance by avoiding
unnecessary computations.
– modularity and conciseness, functional programming promotes modularity through shorter, more
concise code. higher-order functions and nested functions facilitate this.
– lambda expressions, functions can be defined inline using lambda expressions, making code more
expressive and concise.
definitions and values
main function
, def main (args: Array[String]) = {}
new immutable variable
val theAnswer: Int = 42
new mutable variable (not functional)
var fname : String
the structure of a definition
def add (a: Int, b: Int): Int = {a + b}
the conditional expression
if (a>b) “”
else a+b
the conditional expression as a value
val min_value = if (a < b) a else b
the match expression
a match
case 1 => a+b
case 2 => a-b
case 3 => a*b
case _ => a/b
for-loop (not functional)
for (a <- 1 to 7) a+b // 7 included
for (a <- 1 to 9 by 2) a+b // In steps of 2
for (a <- 1 until 8) a+b // 8 excluded
for (a <- 2 to 20 if b>2) a+b // with a condition
multidimensional for-loop
for (a <- 1 to 8; b <- 1 to 8) a+b
classes in scala
class my_name (val cons_field1: cf_type1,...):
val/var my_field1: type1 = value1
val/var my_field2: type2 = value2
def method1 (...) = expr1
end my_name
○ lecture 2: types and data structures
types and the compiler
, a programming language is strongly typed if it enforces strict rules about the types of data and operations
that can be performed on them. this means that you can't perform operations on data types that don't
support those operations without explicit conversion. strong typing helps prevent certain types of errors by
catching them at compile-time or runtime, ensuring safer and more reliable code.
a programming language is statically typed if it is strongly typed and type-checking is performed at compile
time
– type checking involves verifying that the types of variables and expressions are correct based on the
language's rules, without running the program.
– type inference is used to determine the type of each (sub-)expression
scala is a statically typed language that uses local type inference, which means that the compiler can infer
types only in certain parts
– types of variables
– return types of functions
– type parameters when calling polymorphic functions
types and their hierarchy
a type system is a set of rules that assigns a property called type to variables, expressions, functions and
modules. in scala every variable, function and expression has a type that falls under the type hierarchy
Any, the root of the scala type hierarchy, all other types in scala are subtypes of Any.
– AnyVal, represents value types, subtypes include:
– Unit, represents the absence of a meaningful value. only has one value, denoted as ()
– basic value types like Int, Double, Boolean (except String)
– AnyRef, represents reference types. subtypes include:
– all classes, traits, and objects defined in Scala (including String)
– Null, represents the absence of an instance, its only value is null.
Nothing, represents the type of expressions that don't produce a value. it's a subtype of all other types
constructing (complex) types
composite tuple types, combines one or more (possibly different) types into one
val m1: (Int, String) = (1, "jan")
can be indexed
m1(1), returns "jan"
synonym types, introduces a new name for a type to indicate their use
type Year = Int