Die Vorteile funktionaler Programmierung (im speziellen F#)

TL;DR: Weniger Code mit FP und F#.

Was ist es denn genau, was in der Funktionalen Programmierung mit F# den Code, also die Problemlösung, so vereinfacht und verkürzt?

Zu meinem Blog-Post "Less Code and Less Bugs : with Functional Programming languages"
http://functionalsoftware.net/less-code-and-less-bugs-with-functional-programming-languages-589/
bei dem es darum geht, das "weniger Code auch weniger Fehler" hat,
möchte ich hier aufzeigen mit welchen speziellen Sprachkonstrukten F#
  • das Code Verständnis erhöht und

  • dabei prägnanten Code ermöglicht

    (prägnant = knapp, treffend, gehaltvoll.)

    (concise = giving a lot of information clearly and in a few words; brief but comprehensive.)


Es folgt eine Auflistung von ausgewählten F# Features, die in etwa in dieser Reihenfolge aufeinander aufbauen für eine Problemlösungs-Formulierung, und massgeblich für das Code Verständnis und Code Korrektheit dienlich sind.

Was das Code Verständnis und Code Korrektheit am meisten fördert, sind:
  • Funktionen

    Input -> Output : den Output an einem klar definierten Ort im Code und die exakte Type Information des Output.

  • Die Pipe |>

    und Arrow >> Syntax ermöglicht den Daten/Program-Fluss in eine Richtung (ähnlich einem visuellen Flow-Diagram), die der natürlichen Leserichtung von links nach rechts (in unserer Kultur) folgt. Dies erleichtert auch das Naming, weil nicht für jedes Zwischenergebnis das nur einmal weiter gegeben wird ein Name/Bezeichner gefunden werden muss. Je nach verwendeter Namenskonventionen kann dies sehr mühsam zu lesen sein (Wortlängeneffekt, Erinnerungsleistung, Speed-Reading). Statt benannte Zwischenergebnisse Schritt für Schritt anzuwenden x = f() ; y = g(x) ; z = y das definieren was es ist z = f() |> g

  • Higher Order Functions (HOF) https://en.wikipedia.org/wiki/Higher-order_function

    sind Funktionen die Funktionen transformieren und mit minimalem syntaktischen Aufwand codiert werden. Man braucht nur den Funktionsnamen zu übergeben und die Interface-Typen werden von der Type-Inference automatisch ermittelt. Man braucht so nichts zu deklarieren (keine Func, Action, delegate, Interface), einfach nur zusammenbauen.

  • Expressions

    statt Statements (Referential Transparency, Pure Functions) - kurz : das WAS statt das WIE

  • Immutability

    als default und Mutation als Ausnahme.

  • Type Inference https://de.wikipedia.org/wiki/Typinferenz

    nicht weil man weniger Typen Signaturen schreiben muss, sondern weil es "fast" nebensächlich ist und es einfach passen muss, was die Type Inference im Editor während des Code schreibens prüft und signalisiert.

  • Algebraic Data Types (ADT) https://en.wikipedia.org/wiki/Algebraic_data_type

    Prägnanter exakter Daten Design mit Summen- und Produkt-Typen, die auch rekursiv zusammen gestellt (Composition) werden können.

  • Pattern Matching https://msdn.microsoft.com/en-us/library/dd547125.aspx

    ein vom Compiler überwachter Verzweigungs-Control-Flow damit kein (Spezial)-Fall vergessen wird. Man stelle sich das als super intelligentes switch-case Konstrukt vor, das mit den Algebraic Data Types perfekt harmoniert.

  • Computation Expression (CE) https://msdn.microsoft.com/en-us/library/dd233182.aspx

    ermöglicht Code-Plumbing hinter den Kulissen zu erledigen. So ist nur die Essenz in der Computation Expression ersichtlich. Die Logik dahinter, der Computation Builder, kann wiederverwendet werden. Mittels dieser Computation Expression lassen sich elegant z.B. Monaden usw. implementieren.

  • Units of Measure https://msdn.microsoft.com/en-us/library/dd233243.aspx

    vom Compiler geprüfte und berechnete Masseinheiten.

  • Domain Specific Language (DSL), Domänenspezifische Sprache https://de.wikipedia.org/wiki/Dom%C3%A4nenspezifische_Sprache

    F# eignet sich hervorragend um embedded-DSL's zu implementieren, denn man braucht dazu keine Lexer/Parser zu schreiben und kann die DSL in der Entwicklungsumgebung anwenden. Dies ist einfach möglich, da F# nebst kompliliertem Code auch als Scripting verwendet werden kann, das dann wiederum Kompiliert werden kann. Somit hat man für die eigene Domain Specific Language die Spracheigenschaften von F#, wie strict-static-typing und die Entwicklungsumgebung mit samt dem Debugger (wenn nötig). Ein bekanntes und viel angewandtes Beispiel einer embedded-DSL ist FAKE, ein F# Open Source Build Automatisierungs Scripting Tool ("make").


Hier einige Referenzen mit Beispielen die zeigen wie prägnant F# Code sein kann, im Vergleich mit C# Code.

"An F# rewrite of a fully refactored C# Clean Code example"
http://functionalsoftware.net/fsharp-rewrite-of-a-fully-refactored-csharp-clean-code-example-612/

"Analyzing Government Data"
in C# #csharp http://blogs.msdn.com/b/dave_crooks_dev_blog/archive/2015/04/20/intro-to-c-and-analyzing-government-data.aspx
und F# #fsharp http://fssnip.net/qE

"Does the Language You Use Make a Difference (revisited)?"
http://simontylercousins.net/does-the-language-you-use-make-a-difference-revisited/

"F# means less code"
http://fpbridge.co.uk/why-fsharp.html#conciseness

An F# rewrite of a fully refactored C# Clean Code example

Today I stumbled over a C# clean code refactoring example, where I could not resist to do a full rewrite in F# during my break.

The reason for doing this was mainly to present a comparison how does a piece of deeply refactored C# code can look in F#, to motivate others to learn F#, because it makes things (software: thinking, design, coding, review and tests) much simpler and leads to robust solutions.

And the other reason was to show the nice F# syntax coloring that is possible with the Visual F# Power Tools (VFPT) that is an excellent and must have add-on in VS-2015. This screenshot is taken with my customized coloring scheme.

The F# code
F#-Rewrite-of-refactored-c#

The C# code

“C# BAD PRACTICES: Learn how to make a good code by bad example” http://www.codeproject.com/Articles/1083348/Csharp-BAD-PRACTICES-Learn-how-to-make-a-good-code

If you like to have deep detailed F# training please see our F# 2-day course information (German language).

Ok that’s it, I hope you got some new ideas by comparing the F# and C# code.


Update
??.03.2016 : Some more C# refactoring of this example by Ralf Westphahl here http://ralfw.de/2016/03/dont-let-cleaning-up-go-overboard/

Update 26.01.2017 : More C# refactoring by Pete Smith here https://gist.github.com/beyond-code-github/8711794c4d516cb6941d47274884b248

Update 26.01.2017 : Even more C# refactoring of this example by David Arno with C# 7 and Succinc<T> here http://www.davidarno.org/2017/01/26/using-c-7-and-succinct-to-give-f-a-run-for-its-money/

Update 27.01.2017 : An explanation of the F# code by Richard Dalton  http://www.devjoy.com/2017/01/reading-f/

Update 27.01.2017 : Much more C# refactoring by Kenneth Truyers https://www.kenneth-truyers.net/2017/01/27/refactoring-taken-too-far/

Update 28.01.2017 : An similar F# refactoring , with a merged discout logic (do the domain owner like that?) by Jon Harrop https://gist.github.com/jdh30/b01279a6be91467c5887b72a7c2f303e

Update ??.??.2018 : await C# 8 refactoring // ToDo: check if this F# code was also possible with F# 1.9 (2007)





Yet Another Fizz Buzz (YAFB) in F#

Fizz Buzz kennt sicher jeder der auch Hello World kennt.
Meinst wird Fizz Buzz mit der Modulo Funktion gelöst.

Hier wird eine Lösung in F# gezeigt, die endlose Sequenzen verwendet und das Problem sehr beschreibend (deklarativ) löst, ohne dass man die mathematische Bedeutung der Modulo Funktion kennen muss.

Es werden zwei einfache Hilfsfunktionen verwendet die man eigentlich wiederverwendbar in einer Funktions-Bibliothek hinterlegen kann.
Mit der Funktion cycle werden unendlich lange Sequenzen der übergebenen Muster erzeugt.

   let cycle x = Seq.initInfinite (fun _ -> x) |> Seq.concat

Und zipWith verknüpft die Elemente zweier endlosen Sequenzen mittels der übergebenen Funktion f.

   let zipWith f xs ys = Seq.zip xs ys |> Seq.map (fun (x,y) -> f x y)

Nun zum eigentlichen Fizz Buzz, das sehr beschreibend gelöst ist, wie wenn man das Problem jemandem zum ersten mal erklärt.

Die Nummerierung von 1 bis 100 als String.

   let numbers  = seq [1 .. 100] |> Seq.map string

Zwei-mal nichts (“”) dann “fizz” und wiederholend (cycle).
Vier-mal nichts (“”) dann “buzz “und wiederholend (cycle).

   let fizz = seq [“”;””;”fizz”]       |> cycle
   let buzz = seq [“”;””;””;””;”buzz”] |> cycle

Dann die Logik wie alles zusammen agiert:

   let fizzBuzz = zipWith (+) fizz buzz |> zipWith max numbers

Mit zipWith (+) werden die zwei endlosen Sequenzen fizz und buzz elementweise zusammengefügt.
Aus zwei Leer-Strings ergibt sich so wieder ein Leer-String und “fizzbuzz” kommt so zustande “fizz” + “buzz”.

Etwas trickreich oder ungewohnt ist das zipWith max numbers, die Maximum Funktion max ist generisch und in diesem Anwendungsfall wird das Maximum von strings bestimmt. Man kennt das vom Sortieren von strings, diese werden anhand des ASCII codes sortiert.
So gibt auch max den String aus der weiter hinten im Alphabet respektive der ASCII Tabelle befindet.
Und da der Leer-String “” kleiner ist als “1”, “2” und “1” und “2” kleiner sind als “fizz” und “buzz” werden alle “” mit Inhalt also den numbers bestückt und “fizz” und “buzz” sind den Zahlen überlegen.

Und zu guter Letzt erfolgt die Ausgabe des Ergebnis der ersten 20 Elemente

   fizzBuzz |> Seq.take 20 |> Seq.toList

was folgendes anzeigt:

val it : string list =
  [“1″; “2”; “fizz”; “4”; “buzz”; “fizz”; “7”; “8”; “fizz”; “buzz”; “11”;
   “fizz”; “13”; “14”; “fizzbuzz”; “16”; “17”; “fizz”; “19”; “buzz”]


Und der vollständige F# Code:

 
let cycle x         = Seq.initInfinite (fun _ -> x) |> Seq.concat
let zipWith f xs ys = Seq.zip xs ys |> Seq.map (fun (x,y) -> f x y)
let numbers         = seq [1 .. 100]           |> Seq.map string
let fizz            = seq [“”;””;”fizz”]       |> cycle
let buzz            = seq [“”;””;””;””;”buzz”] |> cycle
let fizzBuzz        = zipWith (+) fizz buzz |> zipWith max numbers

fizzBuzz |> Seq.take 20 |> Seq.toList


Ausführbar auf http://tryfs.net/snippets/snippet-tl
Aufgenommen in http://fssnip.net/tl 

Keywords: FizzBuzz without modulo, FizzBuzz ohne Modulo

Funktionen in F#

In der Funktionalen Programmierung (FP) dreht sich alles um Funktionen. Jede Funktion hat Parameter und Rückgabewert. Davon kann man jeweils 0 bis (fast-)beliebig haben.

Es geht im folgenden um: Funktionen, Parameter, Rückgabewert, Tupel, Signaturen

Ein Parameter und ein Rückgabewert ist am einfachsten zu verstehen.

1: 
let sqr (x) = x * x     // x wird quadriert

Zwei Parameter und ein Rückgabewert

1: 
let add (x,y) = x + y   // x und y wird addiert

Hier ist (x,y) ein sogenanntes Tupel, das zwei Werte verbindet.

So ist es möglich mehr als einen Rückgabewert zu verwenden.

Jeder Teilwert eines Tupels kann einen beliebigen Type haben.

Einige nette Wortspielereien zur Auflockerung. Man muss das nicht auswendig wissen. Und nicht verwechseln mit Tribbles ;-)

“Ein 2-Tupel nennt man auch geordnetes Paar,

ein 3-Tupel auch Tripel,

ein 4-Tupel auch Quadrupel,

ein 5-Tupel auch Quintupel und so fort.

Das 0-Tupel heißt leeres Tupel und wird durch () notiert.”

http://de.wikipedia.org/wiki/Tupel

Und das ist jetzt ganz wichtig und kann sonst ein Stolperstein sein beim Erlernen von F#. Darum aufgepasst! Ja und da ist das Verständnis etwas anders als in C# oder C++, aber vollkommen logisch. Die Klammer gehört zum Tupel und nicht etwa zum “Funktions-Parameter-Rupf”. Man kennt es ja eher so von C# oder C++, was genau den selben Code ergibt wie oben.

1: 
let add(x,y) = x + y    // kein space zwischen "add" und "("

Aber beim obigen ist es klarer als Tupel zu erkennen. Wichtig ist den Unterschied zwischen Tupel und dem “Funktionsrumpf” zu erkennen und es NICHT als “Funktionsrumpf” zu deuten!

Zwei Parameter und zwei Rückgabewerte

1: 
let swap (x,y) = (y,x)      // Die Werte im Tupel werden vertauscht

Weil der Rückgabewert hier ein Tupel ist, können zwei Werte zurückgegeben werden.

Drei Parameter und drei Rückgabewerte

1: 
let shift (x,y,z) = (y,z,x)     // Die Werte im Tupel werden nach links geschoben

Kein Parameter und kein Rückgabewert

Jetzt wird es interessant und wird doch vertraut vorkommen.

1: 
let nop () = ()     // keine Operation

Tupel Zusammenfassung

Ein leeres Tupel wird so () geschrieben. Man nennt es Unit oder seltener auch 0-Tupel und damit ist Unit ganz logisch.

Ein 1-Tupel wäre dann z.B. ("X") was ausgewertet val it : string = "X" entspricht, also wie "X". D.h. bei 1-Tupel kann man auch die Klammer weglassen.

1: 
2: 
3: 
let sqr (x) = (x * x)       // val sqr : x:int -> int
let sqr (x) =  x * x        // val sqr : x:int -> int
let sqr  x  =  x * x        // val sqr : x:int -> int

Diese Parameter sind Tupel

1: 
2: 
let add (x,y) = x + y       // val add : x:int * y:int -> int
let add(x,y)  = x + y       // val add : x:int * y:int -> int   // kein space zwischen "add" und "("

Und Achtung, diese Parameter sind KEINE Tupel

1: 
let add  x y  = x + y       // val add : x:int -> y:int -> int

Ein n-Tupel hat dann die Anzahl max 0 (n-1) von Kommas (x , y) in der Schreibweise und Sternchen (x * y) in der Signatur.

Funktions Signatur

Funktionen und deren Signatur. Die Signatur muss man nicht selbst deklarieren, sie entsteht automatisch durch die Komposition der Parameter und des Rückgabewertes.

1: 
2: 
let add  x y  = x + y       // val add : x:int -> y:int -> int
let add (x,y) = x + y       // val add : x:int  * y:int -> int

Bedeutung der Symbole der Signatur

  • x:int : Der Doppelpunkt sagt der Wert x ist vom Type int
  • -> : Der Pfeil trennt die aneinander Reihung von Parametern und Rückgabewert (immer als Letztes)
  • * : Der Stern steht quasi als Ersatz für das Komma im Tupel. Das kommt daher, dass ein Tupel ein Produkt Type ist. Produkt im Sinne von Multiplikation. Mehr dazu in einem späteren Kapitel.

Wichtig: Der Letzte ist immer der Rückgabewert. Und es gibt IMMER einen Rückgabewert, er kann auch Unit () sein.

Hier noch ein Signatur Beispiel mit unit

1: 
let nop () = ()     // val nop : unit -> unit

Wichtig: Kommt kein -> in der Signatur vor, dann ist es keine Funktion, sondern ein Wert.

Unit kann auch im Tupel vorkommen:

1: 
let nop2 () = ((),())   // val nop2 : unit -> unit * unit

Und weil das Wesentliche eines Tupels das Komma ist, kann man die Klammerung in bestimmen Fällen weglassen, ausser bei () und links von = einer let Anweisung.

1: 
2: 
3: 
4: 
let nop2  ()     = ((),())  // val nop2 : unit -> unit * unit
let nop2  ()     =  (),()   // val nop2 : unit -> unit * unit
let nop4 ((),()) =  (),()   // val nop4 : unit * unit -> unit * unit
let nop4  (),()  =  (),()   // error FS0039: The pattern discriminator 'nop4' is not defined

Tupels sind praktisch und ohne grosses Zeremoniell definiert, man braucht nur Komma und Klammer.

Generell können beliebige Tupels in Tupeln vorkommen.

Die weiteren Vorteile zu Tupeln werden dann im Kapitel Pattern Matching behandelt.

Ausstehende Kapitel

Themen die hier angeschnitten wurden.

  • Pattern Matching mit Tupel
  • Produkt Type
val sqr : x:int -> int

Full name: FunktioneninF.sqr
val x : int
val add : x:int * y:int -> int

Full name: FunktioneninF.add
val y : int
val swap : x:'a * y:'b -> 'b * 'a

Full name: FunktioneninF.swap
val x : 'a
val y : 'b
val shift : x:'a * y:'b * z:'c -> 'b * 'c * 'a

Full name: FunktioneninF.shift
val z : 'c
val nop : unit -> unit

Full name: FunktioneninF.nop
val add : x:int -> y:int -> int

Full name: FunktioneninF.add
val nop2 : unit -> unit * unit

Full name: FunktioneninF.nop2
val nop4 : unit * unit -> unit * unit

Full name: FunktioneninF.nop4

Foto einfügen von der Laptop Kamera



In 50 Zeilen F# Code, inklusive WinForm GUI, Spiegelung, Overlay, JPEG Speicherung und Clipboard.



Der Auslöser für diesen Blog war folgendes:
Ich wollte eine kleine Papier-Handskizze mal schnell in ein Dokument einfügen.

Meine Vorstellung davon:

  1. Zettel vor die Kamera halten
  2. Knopf drücken für das Foto
  3. Einfügen im Dokument via Ctrl+v

Realität in Windows 8.1

  • Also auf meinem Windows 8.1 die Camera-App (ist so eine Metro-App) starten.
  • Notiz vor die Kamera halten
  • Auf Fotografieren klicken.
  • Leeres Clipboard?
    • Da ist leider nichts im Clipboard. Hmmm..
  • Wo ist das Foto?
    • Zurück blättern, clicken bis ein Menu erscheint, da gibts ‘Open with’ und ‘Set as Lockscreen’.
    • Auch unter all den Edit Möglichkeiten, kann man das Foto nicht mit Ctrl+C abholen. Hmmm..
  • Wo sind die Fotos gespeichert?
    • Nach Googeln findet sich
    • C:\Users\DeinUser\Pictures\Camera Roll
    • Mit Drag&Drop ins Dokument damit. Hmmm..
  • Spiegelverkehrt?
    • Wenn ich eine Textnotiz vor die Kamera halte, erscheint diese Spiegelverkehrt. Hmmmm...
All die Hmmms.. verraten, ich finde es nicht praktisch! (Useability?)



Wieviele Zeilen F# Code braucht es dazu?

Kern Problem: Kamera Bild auslesen

Varianten:

  1. mit Windows Mitteln, z.B. WinRT (vorweg genommen, es macht kein Spass)
  2. Open Source Library ;-)

1. WinRT für Desktop! (nicht Windows Phone)

Die benötigte Windows.winmd findet man im Windows SDK:
  1. Installieren von Windows Software Development Kit (SDK) für Windows 8.1 http://msdn.microsoft.com/de-DE/windows/desktop/bg162891
  2. Referenzieren von Windows.winmd Winmd ist ein spezielles IL Format für Windows8 Metro Projekte.
    1: 
    #r "C:\ProgramFiles(x86)\WindowsKits\8.0\References\CommonConfiguration\Neutral\Windows.winmd"
  3. Um *.winmd Files zu nutzen muss das Projekt File noch angepasst werden. Innerhalb PropertyGroup ist TargetPlatformVersion zu wie folgt setzen:
    1: 
    <TargetPlatformVersion>8.0</TargetPlatformVersion>
  4. Die WinRT Unterstützung, d.h. das Referenzieren und Compilieren von .winmd Files, in Visual Studio 2012 ist für F# nicht voll unterstützt.
    1: 
    error FS0193: Could not load file or assembly 'file:///C:\Program Files (x86)\Windows Kits\8.0\References\CommonConfiguration\Neutral\Windows.winmd' or one of its dependencies. Operation is not supported. (Exception from HRESULT: 0x80131515)
    Man muss daher einen Teil in C# schreiben, um die WinRT auf dem Desktop zu nutzen. Hmmmm...
Ich suche nach einer reibungslosen Lösung. Also nach Open Source Camera Library suchen. AForge http://www.aforgenet.com/framework/ sieht vielversprechend aus!



2. AForge

  1. Via Nuget folgende ins Projekt holen:
    1: 
    2: 
    3: 
    AForge, 
    AForge.Video, 
    AForge.Video.DirectShow
  2. #r und open (siehe Code) und sehen ob meine Camera (im ASUS ZenBook) gefunden wird
    1: 
    FilterInfoCollection( FilterCategory.VideoInputDevice )
    wunderbar, da ist meine “USB2.0 HD UVC WebCam”
    1: 
    2: 
    3: 
    4: 
    5: 
    val it : FilterInfoCollection =
      seq
        [AForge.Video.DirectShow.FilterInfo
           {MonikerString = "@device:pnp:\\?\usb#vid_04f2&pid_b330&mi_00#7&1fa3e1a8&1&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global";
            Name = "USB2.0HDUVCWebCam";}]
  3. Das Bild kommt per NewFrame Event vom VideoCaptureDevice, also verpacken wir das in einer Funktion:
    1: 
    let onImage f = videoDevice.NewFrame.AddHandler(NewFrameEventHandler f)
  4. Die Bild Bearbeitung für die Anzeige soll wie folgt erfolgen.
    1: 
    let showImage _ e = e |> getImage |> mirror() |> overlay() |> show
    Das Datum-Zeit-Overlay dient nur der Anzeige.
  5. Beim Speichern den mirror() nochmals anwenden, falls dieser eingeschaltet war, damit es nicht spiegelverkehrt ist.
    1: 
    let save = getImage |> mirror() |> toClip |> toFile
  6. Um das Pipelining |> mit den GUI Einstellungen der mirrored und overlayed Checkboxen zu steuern, werden diese so definiert
    1: 
    2: 
    let mirror()  = ifThen (fun () -> mirrored.Checked)  (tee Image.mirror) 
    let overlay() = ifThen (fun () -> overlayed.Checked) (tee Overlay.draw)
    mit der Hilfsfunktion ifThen, die die Transformation mittels der Identity Funktion id abschalten kann. Auf tee wird später eingegangen.
    1: 
    let ifThen b f = if b() then f else id
  7. Um das Pipelining bei Seiten Effekten wie ClipBoard befüllen und File erzeugen im Fluss zu halten,
    1: 
    let save = getImage |> mirror() |> toClip |> toFile
    wird toClip mit Hilfe der tee Funktion definiert.
    1: 
    let tee f x = f x;x
    Damit lässt sich der Input wieder zurückgeben.
  8. So kleine Funktionen wie tee darf man nicht unterschätzen, denn deren Wiederverwendbarkeit ist sehr hoch! So auch beim Erstellen der Bedieneroberfläche, mit einem add das auf tee basiert, werden die Controls an die Form form gebunden. Was sich dann mit dem Rückwärts-Pipe <| ganz elegant liest let shot = add <| new Button
    1: 
    2: 
    3: 
    4: 
    5: 
    6: 
    let add x        = tee form.Controls.Add x
    
    let pic          = add <| new PictureBox(SizeMode=PictureBoxSizeMode.AutoSize)
    let shot         = add <| new Button  (Text="capture"  , Dock=DockStyle.Bottom)
    let mirrored     = add <| new CheckBox(Text="mirrored" , Dock=DockStyle.Bottom, Checked=true)
    let overlayed    = add <| new CheckBox(Text="overlayed", Dock=DockStyle.Bottom, Checked=false)
  9. Der Code ist absichtlich so formatiert, alle = untereinander, dass es auffällt, das er nur aus kurzen und lesbaren “Deklarationen” besteht. Ausführen kann man den Code direkt mit Alt+Enter im Visual-Studio.
     1: 
     2: 
     3: 
     4: 
     5: 
     6: 
     7: 
     8: 
     9: 
    10: 
    11: 
    12: 
    13: 
    14: 
    15: 
    16: 
    17: 
    18: 
    19: 
    20: 
    21: 
    22: 
    23: 
    24: 
    25: 
    26: 
    27: 
    28: 
    29: 
    30: 
    31: 
    32: 
    33: 
    34: 
    35: 
    36: 
    37: 
    38: 
    39: 
    40: 
    41: 
    42: 
    43: 
    44: 
    45: 
    46: 
    47: 
    48: 
    49: 
    50: 
    51: 
    // WebCam capture in 50 lines of F#
    #r "System.Windows.Forms.dll"
    #r @"..\packages\AForge.2.2.5\lib\AForge.dll"
    #r @"..\packages\AForge.Video.DirectShow.2.2.5\lib\AForge.Video.DirectShow.dll"
    #r @"..\packages\AForge.Video.2.2.5\lib\AForge.Video.dll"
    open System
    open System.Drawing
    open System.Windows.Forms
    open AForge
    open AForge.Video
    open AForge.Video.DirectShow
    [<AutoOpen>]
    module Composition   =
        let tee    f x   = f x;x        
        let ifThen b f   = if b() then f else id
    module Camera        =
        let myCamIndex   = 0
        let videoInput   = FilterInfoCollection(FilterCategory.VideoInputDevice).Item myCamIndex
        let videoDevice  = VideoCaptureDevice(videoInput.MonikerString)
        let start _      = videoDevice.Start()
        let stop _       = videoDevice.SignalToStop()
        let onImage f    = videoDevice.NewFrame.AddHandler(NewFrameEventHandler f)
    module Time          =      
        let now()        = DateTime.Now.ToString()
        let nowStamp()   = now().Replace('.','_').Replace(':','_').Replace(' ','_')
    module Overlay       =
        let x,y          = 2.0f, 2.0f
        let font,color   = new Font("Arial", 24.0f), Brushes.Green
        let draw image   = use g=Graphics.FromImage image in g.DrawString(Time.now(), font,color, x,y)
    module Image         =
        type I           = Image
        let toClip (i:I) = tee Clipboard.SetImage i
        let toFile (i:I) = i.Save <| @"C:\temp\cam1" + Time.nowStamp() + ".jpeg"
        let mirror (i:I) = i.RotateFlip RotateFlipType.Rotate180FlipY  
    module Gui           =
        type N           = NewFrameEventArgs
        let form         = new Form(TopMost=true, Visible=true, Width=660, Height=600, Text="WebCamcapturein50linesofF#")
        let add x        = tee form.Controls.Add x
        let pic          = add <| new PictureBox(SizeMode=PictureBoxSizeMode.AutoSize)
        let shot         = add <| new Button  (Text="capture"  , Dock=DockStyle.Bottom)
        let mirrored     = add <| new CheckBox(Text="mirrored" , Dock=DockStyle.Bottom, Checked=true)
        let overlayed    = add <| new CheckBox(Text="overlayed", Dock=DockStyle.Bottom, Checked=false)
        let show image   = pic.Image <- image
        let mirror()     = ifThen (fun () -> mirrored.Checked)  (tee Image.mirror)
        let overlay()    = ifThen (fun () -> overlayed.Checked) (tee Overlay.draw)
        let save         = tee shot.Click.Add (fun _ -> pic.Image |> mirror() |> Image.toClip |> Image.toFile)
        let getImage(e:N)= e.Frame.Clone() |> unbox
        let showImage _ e= e |> getImage |> mirror() |> overlay() |> show
    module App           =
        let run()        = Camera.onImage Gui.showImage;  Gui.form.Closing.Add Camera.stop;  Camera.start()
    App.run()
  10. Es macht was es soll an einen verschneiten Dezember Nachmittag. Es ist kein Production Code (mögliche Leaks und module privacy..)
  11. Weitere Ideen und Anregungen Stereo Image mit mehreren USB WebCams http://www.aforgenet.com/framework/samples/video.html Augmented Reality http://www.aforgenet.com/articles/glyph_recognition/ https://www.youtube.com/watch?v=3sEFk4rRcU4&feature=player_embedded
  12. Andere Publikationen zu WebCam Nutzung mit F#:
    1: 
    2: 
    3: 
    4: 
    5: 
    "UsingaWebcamwithDirectShowNETandF#" 
    DirectShow, DirectShowNET
    Luis Diego Fallas 
    Wednesday, February 24, 2010
    http://langexplr.blogspot.ch/2010/02/using-webcam-with-directshownet-and-f.html
    http://langexplr.blogspot.ch/2010/02/using-webcam-with-directshownet-and-f.html
    1: 
    2: 
    3: 
    4: 
    5: 
    "SilverlightF#(Fsharp+XAML)application.AccesstoWebCamusingonlyF#."
    Windows Phone Silverlight
    Vasily Kalugin 
    3/28/2014
    https://code.msdn.microsoft.com/windowsapps/WebCam-Silverlight-F-sharp-f4f50e85 
    https://code.msdn.microsoft.com/windowsapps/WebCam-Silverlight-F-sharp-f4f50e85
Keywords: Kamera, WebCam, AForge, .Net, dotNet, WinForm, F#, FSharp, Pipelining, id, tee, ifThen, DSL, Wiederverwendbarkeit, webcam fsharp, read camera, cam capture, cam recording, foto image captureing, Foto Aufnahme, integrierte Kamera, ASUS ZenBook camera, USB WebCams, PnP Devices
val not : value:bool -> bool Full name: Microsoft.FSharp.Core.Operators.not
Multiple items val seq : sequence:seq<‘T> -> seq<‘T> Full name: Microsoft.FSharp.Core.Operators.seq ——————– type seq<‘T> = System.Collections.Generic.IEnumerable<‘T> Full name: Microsoft.FSharp.Collections.seq<_>
module Checked from Microsoft.FSharp.Core.Operators
val id : x:’T -> ‘T Full name: Microsoft.FSharp.Core.Operators.id
namespace System
namespace System.Drawing
namespace System.Windows
namespace System.Windows.Forms
Multiple items type AutoOpenAttribute = inherit Attribute new : unit -> AutoOpenAttribute new : path:string -> AutoOpenAttribute member Path : string Full name: Microsoft.FSharp.Core.AutoOpenAttribute ——————– new : unit -> AutoOpenAttribute new : path:string -> AutoOpenAttribute
val unbox : value:obj -> ‘T Full name: Microsoft.FSharp.Core.Operators.unbox
namespace Windows

Die Strukturierung von F# Quellcode

Eine neue Seite zum Thema der Strukturierung von F# Code ist dazugekommen. Es geht um: – warum F# Code so kompakt ist – wie einem die Type-Inferenz behilflich ist sich auf das wesentliche zu Konzentrieren - und wie man dadurch weniger Fehler macht weiterlesen…..

Die Strukturierung von F# Quellcode

F# Code ist sehr kompakt, weil unnötiges und redundantes entfällt. Kurz gesagt, keine ; { } und keine Typen Deklaration. So, nun im Detail alles der Reihe nach:

Keine ;

Das Ende einer Code-Zeile mit ; abzuschliessen ist in F# nicht nötig. Man kann, sieht dann aber aus wie umgewandelter C# oder C++ Code. ; werden verwendet um Aufzählungen von Records, Listen, Arrays etc. zu trennen. Wobei man auch hier, bei nur einem Element pro Zeile den ; nicht zu schreiben braucht.
1: 
2: 
3: 
4: 
5: 
6: 
7: 
let xs = [ 1; 2; 3 ] 
let xs = 
       [ 
        1
        2
        3
       ]

Keine {}

F# Code kommt ohne Code Blöcke aus die in geschweifte Klammern { } gefasst sind. Die Struktur ergibt sich aus der sauberen Einrückungen mittels Tab oder Space (white-space aware code). Die meisten Codierrichtlinen geben ja heutzutage genau eine solche Code Formatierung vor, zusätzlich zu den {} Klammern was redundant ist. Also wieso nicht die Klammer weglassen. Mag sein, dass das für manchen am Anfang etwas komisch aussieht, aber man gewöhnt sich so schnell daran. Kommen in F# doch { } vor, dann handelt sich um einen Record Type oder Computation Expressions

Wie geht denn das ohne { } Code-Blocks ?

Bei gut gestaltetem Code achtet man darauf, dass die Code-Blocks korrekt eingerückt sind. Also z.B.
 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
// C#
void Bar()
{
    bool b = Foo();
    if b 
    {
        FooTrue1();
        FooTrue2();
    }
    else
    {
        FooFalse();
    }
}
Das selbe in F# ist durch weglassen von ; und { } zu erreichen, bei Einhaltung der korrekten Einrückungen.
1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
// F#   
let bar() =
    let b = foo()
    if b then
        FooTrue1()  // Die Einrückung gilt für mehrere Code Zeilen. 
        FooTrue2()       
    else
        FooFalse()
 

Keine Typen Deklaration

Es ist sicher schon aufgefallen, dass bei Funktionen keine Typen angegeben wurden.

Fehlt da dann nicht etwas, wenn im Code die Typen fehlen?

Wie geht das ohne Typen zu deklarieren? Die sogenannte Type-Inferenz findet selbst heraus um welchen Type es sich handeln muss. Man stelle sich vor, man ruft eine API-Funktion auf die data als Parameter entgegennimmt. Diese Funktion verarbeitet int array, somit ist data vom Type int array, also muss man data nicht extra deklarieren. In Visual-Studio oder auch im Blog Code hier, zeigt ein Tooltip über dem jeweiligen Wert oder Funktion dessen Type an, wenn man überprüfen will was es denn nun ist.
Der Code wird dadurch schlanker zu lesen und auch zu schreiben. Wobei Code ja mehr gelesen (Code Review) als geschrieben wird, ist das sehr nützlich, denn Kompakter Code ist schneller zu verstehen!
Kompakter Code, nicht im Sinne von alles auf eine Zeile quetschen, ist schneller zu verstehen, als wenn man Seitenweise lesen muss und sich immer die Typen Deklaration mitdenken muss. Überlassen wir die Typen dem “Compiler”, der prüft diese, und zwar schon vor dem Compilieren, die TypeInference die das erledigt läuft im Editor mit. Passt etwas nicht, wird die Stelle mit der bekannten rote Wellenlinie unterstrichen. Im Tooltip dazu wird erklärt was da nicht passt und was das Problem lösen kann.
Wir kümmern uns darum WAS gemacht wird. Ob alles passt, darum kümmert sich die TypeInference. Es ermöglicht ein abstrakteres Denken, wenn man sich nicht um die Typen Details kümmern muss und spart Entwicklungszeit.
Dies führt zum oft erwähnten positiven Effekt, den man dabei erlebt, dass wenn alles passt, der Code auch gleich erfolgreich kompiliert und meist auch prompt funktioniert. Diese Erfolge motivieren und man will TypeInference nicht mehr missen.

Generische Typen

Selber denkt man eher generisch, also mit generischen Datentypen. Es ist in F# so:
Wenn die Type-Inferenz nicht herausfindet, was es für ein Type sein muss, dann ist der Code automatisch generisch!
Super, man muss also dafür nicht extra überall <T> hinschreiben, das ist dann automatisch so!
1: 
2: 
3: 
4: 
5: 
// C#
void Swap<T>(List<T> list1, List<T> list2)
{
    // code to swap items
}
Und das equivalente generische swap in F#:
1: 
2: 
// F#
let swap (x,y) = (y,x)  // val swap : x:'a * y:'b -> 'b * 'a
und das funktioniert dann auch z.B. mit int List
1: 
2: 
3: 
4: 
let xs = [1; 3]
let ys = [4; 5; 6]
swap (xs,ys)
//val it : int list * int list = ([4; 5], [1; 3])
Auch das Refactoring wird dadurch vereinfacht, man muss die Typen nicht mitschleppen, also lesen und schreiben, umkopieren.
Weil es so einfach ist, braucht man dazu auch keine Refactoringtools! Viele glauben es zu beginn nicht, aber es ist so! Ihr werdet es merken ;-)
Etwas krass ausgedrückt werden Refactoringtool vielfach dazu verwendet, die “nebensächlichen” Teile des Codes zu erzeugen, die man nicht schreiben will. Steht der Code und man muss umbauen, dann müssen all die “nebensächlichen” Teile syntaktisch korrekt verpflanzt werden (Code-Plumbing wie man es auch nennt). Man kann es so sehen:
Die F# Syntax ist so ausgelegt, dass vieles nebensächliche gar nicht vorkommt und so das WAS gemacht wird sofort und klar ersichtlich ist.
Wie z.B.
  • keine “;” am Ende der Codezeile
  • keine { } Code-Blocks
  • keine Typen Deklaration
  • keine Generische Typen Deklaration
Positives Fazit:
Wer weniger schreibt, macht weniger Fehler. Wer weniger lesen muss, findet Fehler eher. Wer weniger lesen muss, arbeitet sich schneller ein. Weniger Code, weniger Bugs. Weniger Code, weniger Code Review. Weniger Code, Entwickler schneller eingearbeitet.
Ja und es geht nicht hauptsächlich darum : “Wer weniger schreibt, ist schneller fertig.” … es ist nett wenn das auch noch so ist ;-)
val xs : int listFull name: StrukturierungvonFQuellcode.xs
type bool = System.BooleanFull name: Microsoft.FSharp.Core.bool
Multiple items module Listfrom Microsoft.FSharp.Collections——————– type List<‘T> = | ( [] ) | ( :: ) of Head: ‘T * Tail: ‘T list interface IEnumerable interface IEnumerable<‘T> member Head : ‘T member IsEmpty : bool member Item : index:int -> ‘T with get member Length : int member Tail : ‘T list static member Cons : head:’T * tail:’T list -> ‘T list static member Empty : ‘T listFull name: Microsoft.FSharp.Collections.List<_>

Der HeartBleed Software Fehler

Und wieder ist, heute am 8 April 2014, ein kapitaler Software Fehler publik geworden. Mich interessiert, wie es zu solchen Fehlern kommt, warum sie so lange nicht erkannt wurden, wie sie zu erkennen sind und am aller wichtigsten, wie man solche Fehler vermeiden kann. Hier kann man übrigens testen ob ein Server von HeartBleed betroffen ist http://filippo.io/Heartbleed/.

Analyse des HeartBleed Bugs

Die beste bisher aufgetauchte Beschreibung des Fehlers habe ich hier gefunden https://news.ycombinator.com/item?id=7549943 von drv und darin die interessanten Stellen fett markiert : TLS heartbeat consists of a request packet including a payload; the other side reads and sends a response containing the same payload (plus some other padding). In the code that handles TLS heartbeat requests, the payload size is read from the packet controlled by the attacker:
1: 
2: 
n2s(p, payload);
pl = p;
Here, p is a pointer to the request packet, and payload is the expected length of the payload (read as a 16-bit short integer: this is the origin of the 64K limit per request). pl is the pointer to the actual payload in the request packet. Then the response packet is constructed:
1: 
2: 
3: 
4: 
/* Enter response type, length and copy payload */
*bp++ = TLS1_HB_RESPONSE;
s2n(payload, bp);
memcpy(bp, pl, payload);
The payload length is stored into the destination packet, and then the payload is copied from the source packet pl to the destination packet bp. The bug is that the payload length is never actually checked against the size of the request packet. Therefore, the memcpy() can read arbitrary data beyond the storage location of the request by sending an arbitrary payload length (up to 64K) and an undersized payload. I find it hard to believe that the OpenSSL code does not have any better abstraction for handling streams of bytes; if the packets were represented as a (pointer, length) pair with simple wrapper functions to copy from one stream to another, this bug could have been avoided. C makes this sort of bug easy to write, but careful API design would make it much harder to do by accident.

Vorkehrung

Die genannte Vorkehrung wäre eine einfache Datenstruktur, ein Tupel (pointer, length), um die zusammengehörenden Daten zu vereinen, damit es nicht zu Verwechslungen kommen kann. In F# Syntax sähe das so aus
1: 
let buffer = (pointer, length)
oder man erzeugt einen Record
1: 
type Buffer = { pointer : int64;  length : int64 }
Ja aber wenn wir schon bei Verwechslungen sind, wie vermeidet man hier dass die length und pointer nicht aus versehen vertauscht zugewiesen werden?
1: 
2: 
let buffer = { l; p }   // Vertauscht! Compiler reklamiert nicht :-(
let buffer = { p; l }   // pointer, length  
Eine Verwechslung der Parameter kann der Compiler so nicht erkennen, es sind ja in diesem Beispiel beides long ! Was kann man dagegen tun? F# hat dazu die sogenannten Units of Measures, also Maßeinheiten, wie Kilogramm und Meter, die ganzen SI-Einheiten sind schon vordefiniert, das nur am Rande, denn wir brauchen hier was anderes. Wie wäre es mit den Einheiten Pointer und Length ? Diese kann man sich wie folgt definieren
 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
// Units of Measures
type [<Measure>] Pointer
type [<Measure>] Length

// Record Type
type Buffer = 
    { pointer : int64<Pointer>
      length  : int64<Length> }

let p = 123456L<Pointer>
let l =    512L<Length>

let buf  = { pointer = p;  length = l }
//    val buf : Buffer = {pointer = 123456L;
//                        length = 512L;}

// Verwechslung bei der Zuweisung
let buf  = { pointer = l;  length = p }
//    error FS0001: Type mismatch. Expecting a
//        int64<Pointer>    
//    but given a
//        int64<Length>    
//    The unit of measure 'Pointer' does not match the unit of measure 'Length'
Als Kommentar sind die Compiler Meldungen angehängt, dort ist schön zu sehen wie F# reklamiert, dass dem Pointer keine Length zugewiesen werden kann.

Noch mehr Bugs?

Interessant ist auch, dass genau da wo der HeartBleed Bug die Ursache hat, schon einmal etwas gefixt wurde am 27 Feb 2012,
“This patch fixes two padding related bugs for the Heartbeat Response messages. For DTLS, the wrong pointer was used, which may overwrite the payload with the random padding. For TLS, there was no random padding at all.” http://marc.info/?l=openssl-dev&m=133035562503173
Das gibt der alten ungeschriebenen Regel wiedereinmal mehr recht:
Da wo ein Bug ist, sind noch mehr.

Fazit

Wichtig war es mir hier anhand aktueller echter Probleme aufzuzeigen, wie man dies elegant und besser lösen kann. Und mit dem Potential das F# mitbringt ist es bestens geeignet um komplexe Probleme einfach und korrekt zu lösen. Man denke dabei auch daran, wie viele Unit-Tests man einsparen kann, wenn der Compiler so strikt prüfen kann.
Multiple items val int64 : value:’T -> int64 (requires member op_Explicit)Full name: Microsoft.FSharp.Core.Operators.int64——————– type int64 = System.Int64Full name: Microsoft.FSharp.Core.int64——————– type int64<‘Measure> = int64 Full name: Microsoft.FSharp.Core.int64<_>
Multiple items type MeasureAttribute = inherit Attribute new : unit -> MeasureAttributeFull name: Microsoft.FSharp.Core.MeasureAttribute——————– new : unit -> MeasureAttribute

Interpretation der F# Ausdrücke

  In F# ist alles mit Ausdrücken (expressions) aufgebaut, wo andere Sprachen Statements (statements) verwenden. Diese Ausdrücke werden in folgender Reihenfolge ausgewertet:
von links nach rechts von oben nach unten
Also nichts ungewöhnliches, genau wie die Deutsche Sprache geschrieben wird. D.h. alles was verwendet wird ist oberhalb oder in anderen Modulen (oder Assemblies) definiert. Die einzige Ausnahme ist bei type member, also Klassen Member, deren Reihenfolge willkürlich sein kann. Wieder mit der Ausnahme, dass der namenlose Konstruktor (also der, der nicht New heisst) als erstes kommt. In einem Projekt sind die Sourcefiles auch strikt hierarchisch geordnet. Also das unterste kann die oberen mit open (analog C# using) einbinden, nicht umgekehrt.

Was macht man, wenn es scheinbar nicht möglich ist so strikt zu strukturieren?

Wie geht das bei zirkulären Abhängigkeiten (circular dependency)? Für Typen- type und Funktions- let Definitionen innerhalb eines Moduls gibt es das and Konstrukt, das jeweils die abhängigen type oder let Schlüsselworte ersetzt. Dies erlaubt es wechselseitig Anhängige Definitionen zu bilden. So sind zirkuläre Definitionen möglich (ohne Vorausdeklarationen und Mutation).

Wieso ist das so strikt und stur?

Einerseits ist es so, dass es für den Compiler und vor allem für die Type Inferenz so sicher einfacher ist. Das könnte man als Nachteil sehen, dass man gezwungen wird den Aufbau so zu machen. Die Erkenntnis ist aber, dass ein so aufgebautes Software System sehr klar ist. Hierarchisch strikt durchschaubar. Keine Spagetti-hafte Kreuz Abhängigkeiten zwischen den Modulen. Was hier detailliert analysiert wird.
Zirkuläre Abhängigkeit von Komponenten verhindert die Schichten-Bildung (Layering) und ist somit schlechter testbar, wartbar, erweiterbar und wiederverwendbar.

Abhilfe für zirkuläre Abhängigkeiten

Die Tricks sind:
  • Die Datentypen von den Funktionen trennen. Was genau die Umkehrung vom Objektorientierten Design ist, also die Memberfunktionen aus den Klassen reissen und die herausfallenden mutierenden Zustände vermeiden. Diese Funktionen generisch machen und so deren Wiederverwendbarkeit ermöglichen. Was bei Funktionaler Programmierung typischerweise der Fall ist, weil die Daten immutable sind, funktioniert das einfach so.
Daten sind Programmiersprachen unabhängig, Objekt Klassen nicht. Man denke an die Speicherung.
  • Abstrahieren der Datentypen, möglichst alle Funktionen für generische Typen auslegen. Was in F# automatisch passiert, wenn man keinen Type spezifiziert.
Werte sind die Besten Interfaces.

Nicht aufgeben

Also wenn man etwas versucht das mit der strikten Hierarchischen Struktur nicht klar kommt, dann sollte man sich genau überlegen was es ist, das nicht in diese Form passt. Meist zeigt sich, dass es auch anders geht. Und so kommt es automatisch zu guten einfachen Designs.
Der Vorteil ist, dass man den Code so wie ein Buch lesen kann und die Reihenfolge ist logisch aufbauend auf schon bekanntem. Man muss nicht im Code oder den Komponenten herum springen um ihn zu lesen und zu verstehen.
Man kann es vom Kleinen zum Grossen lesen, oder umgekehrt beim Grossen beginnen um sich einen Überblick zu verschaffen und dann in die Details gehen. Man weiss wo man starten kann. Dies ermöglicht einem neuen Teammitglied den schnellen Einstieg in eine vorhandene Code-Basis. Man muss nicht erst Design Dokumentation lesen, falls solche vorhanden ist und sich mit dieser anfreunden oder erlernen.

Beispiel zu wechselseitig Anhängigen Typen

Die in diesem Beispiel voneinander abhängigen Typen sind Person und Ortschaft, da eine Person ein Wohnsitz hat und eine Ortschaft Einwohner hat.
 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
31: 
32: 
33: 
34: 
35: 
36: 
37: 
38: 
39: 
40: 
41: 
42: 
43: 
44: 
// Datentypen (Records)
type Person =
    { name      : string
      wohnort   : Ortschaft  
    }
and Ortschaft =
    { name      : string
      einwohner : Person list
    }

// Funktionen
let getPerson name ort =
    { Person.name = name; 
      wohnort = { Ortschaft.name = ort; einwohner = [] } 
    }

let lebtInOrtschaft ortschaftName person = 
    person.wohnort.name = ortschaftName

let einwohnerVon ortschaftName personen =
    personen |> List.filter (lebtInOrtschaft ortschaftName) 

let getEinwohnerZahl (ort : Ortschaft) =
    ort.einwohner.Length

let getOrtschaft ort personen =
    { name      = ort
      einwohner = personen |> einwohnerVon ort 
    }

// Aufbau der Datenstruktur
let Fritz  = getPerson "Fritz" "Zürich"
let Hans   = getPerson "Hans"  "Genf"  
let Max    = getPerson "Max"   "Genf"  

let Personen = [ Fritz; Hans; Max ] 

let Genf   = getOrtschaft "Genf"    Personen
let Zurich = getOrtschaft "Zürich"  Personen

let Ortschaften = [Genf; Zurich]

// Anwendung
List.map getEinwohnerZahl Ortschaften   // = [2; 1]
Aufgabe : Max zieht um von Genf nach Zürich. Wie bekommt man das hin, weil ja alles unveränderlich (immutable) ist? Lösung : Alle Veränderungen werden in diesem Beispiel hier mit angehängtem ' an den Wertenamen markiert. Das ist reine Dekoration, man könnte auch immer eine 2 anhängen. Wichtig ist, es entsteht ein neuer Wert, hier mit neuem Namen.
1: 
2: 
3: 
4: 
5: 
6: 
7: 
let Max'         = { Max with wohnort = Zurich }
let Personen'    = [ Fritz; Hans; Max' ] 
let Genf'        =  getOrtschaft "Genf"   Personen'
let Zurich'      =  getOrtschaft "Zürich" Personen'
let Ortschaften' = [ Genf'; Zurich' ]

List.map getEinwohnerZahl Ortschaften' // = [1; 2]
Jetzt könnte man meinen dies sei Speicherverschwendung, immer alles neu aufzubauen?
Der Code spezifiziert Was gemacht wird, nicht das Wie. Es wird intern nicht alles neu Aufgebaut, nur die Änderungen. Die Liste ist eine spezielle Funktionale Datenstruktur, die weil ja alles immutable ist, intern auf die bereits existenten unveränderlichen Daten verweist. Somit ist die Liste natürlich auch Thread-Safe.
Siehe auch Vorteile Funktionale Datenstruktur und Chris Okasaki’s 1998 Buch “Purely functional data structures” und Chris Okasaki’s Blog Grob gesagt nur die neue Mutation, also der Max' benötigt zusätzlichen Speicher. Ein Vorteil ist so auch, dass man die ganze History immer vorhanden ist. Gewisse Anwendungsbereiche leben davon, wie z.B. Source Control, Bankkonten. Da wird die ganze Geschichte aufbewahrt und nicht einfach die Speicherstelle Kontostand verändert. Dieses Beispiel konkurriert natürlich nicht mit speziell dafür (was immer genau die Anforderungen sind?) ausgelegten Datenstrukturen. Und F# kann auch Daten ändern, siehe ref und mutable, dazu später mehr.
Erinnern wir uns, das Beispiel zeigt wie man wechselseitig abhängige Typen definiert, erzeugt und handhabt.
type Person = {name: string; wohnort: Ortschaft;}Full name: InterpretationderFAusdrücke.Person
Person.name: string
Multiple items val string : value:’T -> stringFull name: Microsoft.FSharp.Core.Operators.string ——————– type string = System.String Full name: Microsoft.FSharp.Core.string
Person.wohnort: Ortschaft
type Ortschaft = {name: string; einwohner: Person list;}Full name: InterpretationderFAusdrücke.Ortschaft
Ortschaft.name: string
Ortschaft.einwohner: Person list
type ‘T list = List<‘T>Full name: Microsoft.FSharp.Collections.list<_>
val getPerson : name:string -> ort:string -> PersonFull name: InterpretationderFAusdrücke.getPerson
val name : string
val ort : string
val lebtInOrtschaft : ortschaftName:string -> person:Person -> boolFull name: InterpretationderFAusdrücke.lebtInOrtschaft
val ortschaftName : string
val person : Person
val einwohnerVon : ortschaftName:string -> personen:Person list -> Person listFull name: InterpretationderFAusdrücke.einwohnerVon
val personen : Person list
Multiple items module Listfrom Microsoft.FSharp.Collections ——————– type List<‘T> = | ( [] ) | ( :: ) of Head: ‘T * Tail: ‘T list interface IEnumerable interface IEnumerable<‘T> member Head : ‘T member IsEmpty : bool member Item : index:int -> ‘T with get member Length : int member Tail : ‘T list static member Cons : head:’T * tail:’T list -> ‘T list static member Empty : ‘T list Full name: Microsoft.FSharp.Collections.List<_>
val filter : predicate:(‘T -> bool) -> list:’T list -> ‘T listFull name: Microsoft.FSharp.Collections.List.filter
val getEinwohnerZahl : ort:Ortschaft -> intFull name: InterpretationderFAusdrücke.getEinwohnerZahl
val ort : Ortschaft
property List.Length: int
val getOrtschaft : ort:string -> personen:Person list -> OrtschaftFull name: InterpretationderFAusdrücke.getOrtschaft
val Fritz : PersonFull name: InterpretationderFAusdrücke.Fritz
val Hans : PersonFull name: InterpretationderFAusdrücke.Hans
val Max : PersonFull name: InterpretationderFAusdrücke.Max
val Personen : Person listFull name: InterpretationderFAusdrücke.Personen
val Genf : OrtschaftFull name: InterpretationderFAusdrücke.Genf
val Zurich : OrtschaftFull name: InterpretationderFAusdrücke.Zurich
val Ortschaften : Ortschaft listFull name: InterpretationderFAusdrücke.Ortschaften
val map : mapping:(‘T -> ‘U) -> list:’T list -> ‘U listFull name: Microsoft.FSharp.Collections.List.map
val Max’ : PersonFull name: InterpretationderFAusdrücke.Max’
val Personen’ : Person listFull name: InterpretationderFAusdrücke.Personen’
val Genf’ : OrtschaftFull name: InterpretationderFAusdrücke.Genf’
val Zurich’ : OrtschaftFull name: InterpretationderFAusdrücke.Zurich’
val Ortschaften’ : Ortschaft listFull name: InterpretationderFAusdrücke.Ortschaften’

Starten mit F#

Um zu starten und reinzuschnuppern braucht man nichts zu installieren. Entwicklungsumgebung also Code Editor und Compiler als Webseite gibt es gratis und ohne Anmeldung hier: Dieser läuft auf allen Systemen, auch für Apple iOS (iPad ohne Silverlight) Diese benötigen einen Web-Browser mit installiertem Microsoft Silverlight

Probiert aus, was euch am meisten zusagt. Unten sind zwei kurze Code Schnipsel zum Ausprobieren. Diese können einfach in die obigen Editoren kopiert werden um sie auszuführen.

Update: Gute Neuigkeiten, seit 12. Nov. 2014 ist von Microsoft das Visual Studio Community 2013 with Update 4 gratis hier erhältlich, die Installation oder das ISO Image hat einen Grösse von 6.9 GB. Mehr dazu  “Visual Studio Community 2013 – A Full-Featured IDE – FREE – Start coding the app of your dreams for Windows, Android, and iOS.” und “Opening up Visual Studio and .NET to Every Developer, Any Application”


Update: F# für Linux, Mac, Windows

Free Monodevelop wie hier beschrieben  http://www.monodevelop.com/download heisst dann (etwas verwirrend weil es umgetauft wurde) XamarinStudio.

Installation auf Windows 8.1 (Für andere F# Installationen empfiehlt sich http://fsharp.org/use/ )
Stand: 27.6.2015
Mit total ca. 100 MB download ist man dabei und die Installationszeit ist freundlich kurz, jedoch in 4 Schritten, aber alles ohne Windows zu booten.
1. .NET Framework 4.5:
    Download  http://go.microsoft.com/fwlink/p/?LinkId=397703
2. Install the free Visual F# Tools 3.1.2 from Microsoft
    Download (ca. 28 MB) http://www.microsoft.com/download/details.aspx?id=44011
3. GTK# for .NET 2.12.25:
    Download (ca. 25 MB) http://download.xamarin.com/GTKforWindows/Windows/gtk-sharp-2.12.25.msi
4. Xamarin Studio:
    Download (ca. 45 MB) http://download.xamarin.com/studio/Windows/XamarinStudio-5.9.2.4-0.msi

Im Xamarin Studio erstes F# Projekt erstellen (danach merkt er sich das)
Xamarin Studio F# Einstellungen
Selektierten F# Code im Editor ausführen mit Ctrl + Enter (nicht Alt + Enter wie in Visual Studio).


F# Beispiele

Auflistung .NET Assemblies

1: 
2: 
3: 
4: 
let methods =
    System.AppDomain.CurrentDomain.GetAssemblies()
    |> Seq.map (fun asm -> asm.GetTypes())
printfn "%A" methods
Die Ausgabe (gekürzt)
seq [[|System.Object; System.Runtime.Serialization.ISerializable; System.Runtime.InteropServices.Exception; System.Exception; System.Exception+_RestrictedErrorObject; System.ValueType; System.IComparable; System.IFormattable; System.IConvertible; …

Gray-Code Erzeugung

1: 
2: 
3: 
let (&) x = List.map((+)x)
let g x = ("0" & x) @ ("1" & (List.rev x))
printf "%A" ([""] |> g |> g |> g)
Gibt die 3-stelligen Gray-Codes aus. Für jedes |> g eine Stelle. Die Gray-Codes sind so angeordnet, dass immer nur ein Bit wechselt.
[“000″; “001”; “011”; “010”; “110”; “111”; “101”; “100”]
Tipp : Es ist klar das man als F# Neuling dies nicht gleich verstehen kann. Bitte nicht abschrecken lassen. Das Ergebnis soll etwas zum Staunen anregen. Es ist schon etwas kryptisch, aber es soll zeigen wie elegant und kompakt man Funktionen verknüpfen kann. Wenn man überhaupt nicht verstehen kann wie es funktionieren könnte, dann implementiere man den Gray-Code in seiner aktuell verwendeten Programmiersprache. Und pausiere das Lesen hier. STOP! nicht weiterlesen. Auflösung : Man wird erkennen, dass der Algorithmus auf Zeile 2 implementiert ist. D.h. es ist eher eine Spezifikation des Algorithmus, als eine imperative Abhandlung. Auf Zeile 1 wird ein Hilfsoperator & definiert der ein String mit einer String Liste verbindet. Der @ Operator verbindet zwei Listen. Und [ ] definiert eine Liste gefüllt mit einem leeren "" String. Der Pipe-Operator |> schiebt die Werte in die Funktion g, die der Generator für die nächste Stufe des Gray-Codes ist. Man könnte auch g(g(g([""]))) schreiben anstatt [""]|>g|>g|>g aber letzteres zeigt den Daten-Fluss viel intuitiver an. List.rev kehrt eine Liste um. Das printf macht die Ausagbe und mit "%A" als alles Formatierer.

F# Sprach Konstrukte

Wer schon eine Programmiersprache kennt, für den ist es sowieso einfach. Die vier wesentlichen Sprach Konstrukte sind
  • let : Zuweisung von Values und Funktionen
  • fun : Anonyme Funktion
  • type : Type Definition (Record, Class, Discriminated Union (DU))
  • match : Fallunterscheidung (das machtige “if” und “switch” in einem)
Zur Sprache F# selbst wird hier keine detaillierte Schritt für Schritt Abhandlung der kompletten Syntax gezeigt. Man findet die komplette F# Sprach Referenz “The F# Specification Language” als Nachschlagewerk gratis hier als PDF oder als Webseite .

Ausblick

Was macht F# so anderst?

Es sind die Kleinen, meist unterschätzten, Unterschiede und deren Konzepte in der Basis die, die Möglichkeiten in Höheren Ebenen wie Abstraktion und Komposition erst ermöglichen. Z.B. dass alle Values immutable sind, immer zugewiesen sind und es darum keine null (NULL) Referenzen vorkommen, dazu später mehr. Ich nenne sie Micro Patterns (Mikro Muster), es sind winzige Patterns mit wenig Code, die im Zusammenspiel, also der Komposition die wahre Funktion entfalten. Im Gegensatz zu den bekannten Objektorientierten Entwurfsmuster (Design Patterns), die aus mehreren zusammen agierenden Klassen bestehen, mit einigen Zeilen an Code, die sich pro Klasse über mehrere Files erstrecken können, so dass man, um den Zusammenhang zu erkennen, meist zu visuellen Darstellungen mittels UML Diagrammen greift.
Jetzt nehme ich etwas vorweg, der Clou an diesen Micro Patterns ist, dass diese das selbe wie die “riesigen” GoF Design Patterns, aber mit viel viel weniger Code (und ohne Klassen) bewerkstelligen können und somit überschaubar bleiben. Die Muster sind manchmal so klein, dass sie gleich als Sprachbestandteil von F# aufgehen.
Weiter geht’s dann im nächsten Blog.
val methods : seq<System.Type []>Full name: StartenvonF.methods
namespace System
type AppDomain = inherit MarshalByRefObject member ActivationContext : ActivationContext member AppendPrivatePath : path:string -> unit member ApplicationIdentity : ApplicationIdentity member ApplicationTrust : ApplicationTrust member ApplyPolicy : assemblyName:string -> string member BaseDirectory : string member ClearPrivatePath : unit -> unit member ClearShadowCopyPath : unit -> unit member CreateComInstanceFrom : assemblyName:string * typeName:string -> ObjectHandle + 1 overload member CreateInstance : assemblyName:string * typeName:string -> ObjectHandle + 3 overloads …Full name: System.AppDomain
property System.AppDomain.CurrentDomain: System.AppDomain
System.AppDomain.GetAssemblies() : System.Reflection.Assembly []
module Seqfrom Microsoft.FSharp.Collections
val map : mapping:(‘T -> ‘U) -> source:seq<‘T> -> seq<‘U>Full name: Microsoft.FSharp.Collections.Seq.map
val asm : System.Reflection.Assembly
System.Reflection.Assembly.GetTypes() : System.Type []
val printfn : format:Printf.TextWriterFormat<‘T> -> ‘TFull name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn
val x : string
Multiple items module Listfrom Microsoft.FSharp.Collections ——————– type List<‘T> = | ( [] ) | ( :: ) of Head: ‘T * Tail: ‘T list interface IEnumerable interface IEnumerable<‘T> member Head : ‘T member IsEmpty : bool member Item : index:int -> ‘T with get member Length : int member Tail : ‘T list static member Cons : head:’T * tail:’T list -> ‘T list static member Empty : ‘T list Full name: Microsoft.FSharp.Collections.List<_>
val map : mapping:(‘T -> ‘U) -> list:’T list -> ‘U listFull name: Microsoft.FSharp.Collections.List.map
val g : x:string list -> string listFull name: StartenvonF.g
val x : string list
val rev : list:’T list -> ‘T listFull name: Microsoft.FSharp.Collections.List.rev
val printf : format:Printf.TextWriterFormat<‘T> -> ‘TFull name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printf