- title : Funktionale Programmierung mit F#
- description : Funktionale Programmierung mit F#
- author : Max Malook
- theme : night
- transition : default
Es geht um die Kernkonzepte der funktionalen Programmierung. Dazu gehört zum Beispiel Immutability, Data Types, Partial Application und Pattern Matching die in praktischen Übungen angewendet werden. Dabei werden auch F#-spezifische Sprachfeatures wie Active Patterns und Object Expressions behandelt.
- Spass und ein eigenes Notebook
- F# Entwicklungsumgebung, Auswahl und Installationsanteilung für Windows, Mac oder Linux
- function definition
- functional purity
- pure - no side effects
- expressions - immutable values
- mutable keyword [F#]
- reference cells [F#]
- first class functions
- higher order functions
- lambdas
- partial application
- Tacit programming, i.e. point-free style
- composition
- pipelines [F#]
- recursion
- type system
- type inference
- different types
- design for correctness
- make illegal states unrepresentable
- unit [F#]
- object expressions [F#]
- type extensions [F#]
- currying = first class functions + tuple
- pattern matching
- active patterns [F#]
- Funktion ist die Mutter von Allem
- EVA = Eingabe => Verarbeitung => Ausgabe
- Eine Funktion, aka Abbildung, in der Mathematik ist genau dadurch definiert: eine Definitionsmenge, eine Zielmenge und eine Abbildungsvorschrift.
- Eine Funktion ist eine Relation zwischen der Definitions- und Zielmenge, die linkstotal und rechtseindeutig ist. (What the hack?)
- Linkstotal: Jeder Wert der Definitionsmenge kann als Eingabe verwendet werden. Die Funktion ist für jeden Wert definiert.
- Rechtseindeutig: Jedem Wert der Definitionsmenge ist nur genau ein Wert der Zielmenge zugeordnet.
Beispiel:
[lang=haskell]
// Nachfolger einer natuerlichen Zahl
f(x) = x + 1
Eigenschaften, welche eine Funktion erfüllen muss:
- bei gleicher Eingabe, liefert diese gleiche Ausgabe
= determiniert (weniger als deterministisch) - keine Seiteneffekte
(besser als Nebeneffekte genannt, und noch besser als Wirkung)
= wirkungsfrei, ändert keinen Programmzustand (IO, Zeit o.ä.)
- Werte werden an Namen gebunden, nicht zugewiesen!
- Es gibt keine Variablen!
Beispiel:
[module=Expressions]
let f x = x + 1
let f x = x * 2 // Fehler, da f nicht geändert werden darf.
- Man kann in F# auch imperativ programmieren.
- Also Variablen haben, jedoch gilt es diese zu vermeiden.
Beispiel:
[module=Mutables]
let mutable x = 1
x <- x + 1
- Manchmal ist
mutable
nicht genug und man brauch eine Referenzzelle. - Der Compiler weist darauf hin, z.B. in einer Closure (seq-Expression).
Beispiel:
[module=RefCells]
let x = ref 1
x := !x + 1
Wenn man Funktionen als Funktionseingabe bzw. -ausgabe verwenden kann.
Beispiel:
[module=HighOrderFuncs]
let evalWith2AndAdd5 f = f 2 + 5
let succ x = x + 1
evalWith2AndAdd5 succ
let add x y = x + y
Anonyme Funktionen
Beispiel:
[module=Lambdas]
let add x y = x + y
let add x = fun y -> x + y
let add = fun x -> (fun y -> x + y)
Binden einer Funktionseingabe an einen speziellen Wert.
Beispiel:
[module=PartialApplication]
let succ x = add 1 x
let succ x = 1 + x
Also eta-reduction
Beispiel:
[module=Tacit]
let succ x = add 1 x
let succ = add 1
let succ x = 1 + x
let succ x = (+) 1 x
let succ = (+) 1
Komposition bzw. Verkettung von Funktionen.
[lang=haskell]
f: a -> b, g: b -> c => (>>) f g: a -> c
(>>) f g x = g(f(x))
Beispiel:
[module=Composition]
let succ x = 1 + x
let square x = x * x
let squareOfSucc = succ >> square
let succOfSquare = square >> succ
squareOfSucc 2
succOfSquare 2
Werte an Funktionen übergeben.
[lang=haskell]
v: a, f: a -> b => (|>) v f: a -> (a -> b) -> b
(|>) v f = f v
Beispiel:
[module=Pipelines]
let succ = (+) 1
let square x = x * x
2 |> succ
2 |> square
2 |> succ |> square = squareOfSucc 2
2 |> square |> succ = succOfSquare 2
- Abbruchbedingung und Rekursionsvorschrift!
- Explizite Kennzeichnung rekursiven Funktionen.
Beispiel:
[module=Recursive]
let rec factorial n =
if n = 1 then 1
else n * factorial (n - 1)
- Typeninferenz ist eine schöne Sache, damit brauchen wir fast nie den Datentypen angeben.
- Bisher haben wir bei der Definitionen der Funktionen nie die Datentypen angegeben.
- Ist sehr reichhaltig und das sollte ausgenutzt werden.
- Es gibt die allbekannten primitiven Typen wie
- int, float, bool und string.
- Dann haben wir auch schon Funktionstypen kennen gelernt, wie
- int -> int.
- Natürlich gibt es das bekannte Array und die unbekannte Liste.
- Tuple, mit diesem können Daten unterschiedlichen Typs zusammengefasst werden.
Beispiel:
[module=Tuples]
type Person = string * int
let trainer = ("Max", 33)
let name = fst trainer
let age = snd trainer
let name, age = trainer
- Record, auch mit diesem können Daten unterschiedlichen Typs zusammengefasst werden, bietet einwenig mehr als Tuple.
Beispiel:
[module=Records]
type Person = { Name: string; Age: int }
let trainer = { Name = "Max"; Age = 33 }
let trainerAYearAgo = { trainer with Age = 32 }
let name = trainer.Name
let age = trainer.Age
let { Name = name; Age = age } = trainer
let { Name = name } = trainer
let { Age = age } = trainer
- Discriminated union, mit diesem können unterschiedliche Ausprägungen zu einem zusammengefasst werden.
Beispiel:
[module=DiscriminatedUnions]
type Person =
| Trainer
| Student
type Point = {X: float; Y: float}
type Shape =
| Circle of center: Point * radius: float
| Rect of corner: Point * width: float * height: float
- Ist wie Nullable, geht aber auch mit Klassen.
Just say NO to NullPointerExceptions @jessitron
Beispiel:
[module=Options]
type Option<'t> =
| Some of 't
| None
- Tuple und Record sind die sogenannten Produkttypen.
- Discriminated union ist ein Summentyp.
Mit vorgestellten Möglichkeiten ist es ein Leichtes ein korrektes Abbild der Domäne zu schaffen, damit nur die Zustände darstellbar werden, welche auch tatsächlich erlaubt bzw. möglich sind.
Manchmal, wenn man Funktionen mit Wirkung haben muss,
z.B. IO, Zeit oder Random,
dann gibt es entweder keine Eingabe oder keine Ausgabe.
In anderen Sprachen wird dafür entweder ein Schlüsselwort oder gar Konstrukt verwendet.
In F# gibt es dafür einen speziellen Datentypen
unit
mit einzigem Wert ()
.
Anonyme Objekte
Beispiel:
[module=ObjectExpresions]
let mock = { new IInterface with member this.Method() = "3" }
Erweiterungsmethoden
Beispiel:
[module=TypeExtensions]
type System.String with
member x.FirstChar() = x.Substring(0, 1)
Beispiel:
[module=Curring]
let add(x, y) = x + y
let addCurried x y = add (x, y)
Switch statements on steroids.
Beispiel:
[module=Patterns]
let maybe = Some "Name"
match maybe with
| Some value -> value
| None -> "Unbekannt"
Switch statements on steroids.
Beispiel:
[module=Patterns]
match System.Int32.TryParse("Max") with
| true, value -> value
| false, _ -> -1
Erweiterung des Matching mit eigenen Fällen.
Beispiel:
[module=ActivePatterns]
let (|IsFizz|) n = n % 3 = 0
let (|IsBuzz|) n = n % 5 = 0
let number = 5
match number with
| IsFizz -> "Fizz"
| IsBuzz -> "Buzz"
| number -> string number
Erweiterung des Matching mit eigenen Fällen.
Beispiel:
[module=ActivePatterns]
let (|Number|_|) (s:string) =
match System.Int32.TryParse(s) with
| true, value -> Some value
| false, _ -> None
let text = "5"
match text with
| Number x -> x + 1
| _ -> -1
Erweiterung des Matching mit eigenen Fällen.
Beispiel:
[module=ActivePatterns]
let (|Fizz|Buzz|FizzBuzz|None|) n =
match n with
| IsFizz & IsBuzz -> FizzBuzz
| IsFizz -> Fizz
| IsBuzz -> Buzz
| _ -> None
let number = 60
match number with
| FizzBuzz -> printfn "number is FizzBuzz"
| Fizz -> printfn "number is Fizz"
| Buzz -> printfn "number is Buzz"
| None -> printfn "number is None"
- Functional Principles for OO Devs by Jessica Kerr
- Active Patterns explained by Richard Dalton
- FP Europe 2014 Tour by Mathias Brandewinder
- indentation is for scope
- modules are for code organization
- object oriented programming in F#
- unit of measure