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

Functional Programming in F# für .NET – Kursinhalte

Die Kurs Inhalte können variieren, je nach Wissensstand, Zeitrahmen und Trainingsschwerpunkten.
Es wird Wert gelegt auf die Grundlagen und dass man das Gelernte sofort anwenden kann.

Sofort Produktiv

  • einfache und mächtige F# Syntax, wenig Code, einfaches Refactoring
  • Code ausführen während man ihn schreibt
  • effiziente und gut verständliche Datenmodellierung (Domain Driven Design (DDD)) mit Algebraischen Daten Typen die automatisch Strukturellen Vergleich und Hashing bieten.
  • Masseinheiten werden vom Compiler bei Übergabe und Berechnungen auf Korrektheit geprüft (Meter, Kilogramm, Sekunde, Pixel, Dollar, SelbstDefiniert, etc.)
  • mit F# Type Providern Ihre Daten automatisch typisiert im Code mit IntelliSense anwenden. Type Provider sind ein einzigartiges Feature von F# (seit 2010, ich konnte es kaum glauben als ich es das erste mal 2010 gesehen habe, It’s magic!), das keine andere Programmiersprache bisher ermöglicht (mit Ausnahme von IDRIS seit März 2015).
  • Erstellen von eigenen Domain Specific Languages (DSL)
  • F# ist in Visual Studio (seit 2010) schon enthalten
  • Kompatibilität, .dll’s können von C#, VB.NET und umgekehrt verwendet werden.

Wichtige Grundlagen der Funktionalen Programmierung (FP)

  • Immutability: verstehen warum Immutability gut ist und hilft Probleme einfacher zu lösen und Code besser zu verstehen. Immutable Daten Strukturen
  • Type Inference : verstehen und Nutzen erkennen
  • Partial Application und Curring
  • Rekursion: rekursive Funktionen und rekursive Datentypen
  • Higher Order Functions : map, reduce, filter, etc.
  • Pattern Matching
  • Referential Transparency
  • Lazyness: Seq, lazy
  • Memoziation
  • Algebraischen Daten Typen (ADT) (sind keine POCOs (Plain-Old CLR Objects))

Wesentliche Unterschiede von F# zu C#

  • null als ungültiger Wert (nicht als gültiger default Wert von Referenz Typen in C#)
  • unit als leerer Wert (nicht void als kein Wert wie in C#, denn void und async vertragen sich nicht gut in C#)
  • async als Library (nicht als async und await Sprachkonstrukte wie in C#)
  • lock als Funktion (nicht als Statement wie in C#)
  • choice für Exception Handling (Exception free programming)

Automatisiertes Testen

  • interaktives Testen im REPL
  • automatisches Erzeugen von Testdaten und Ermittlung des kleinsten Datensatzes, der die Fehler-Analyse vereinfacht mit FsCheck
  • Testen von Webseiten mit Canopy (Automatisiertes Surfen)

Parallel Programming und Asynchronous Programming (Concurrent Programming)

  • Parallelen Code schreiben, dem man trauen kann (immer richtiges Ergebnis)
  • Async Code composition
  • Actors (F# MailboxProcessor)

Web Programmierung (wird im Grundkurs nur gestreift)

  • F# Code zu JavaScript compilieren (mit FunScript oder WebSharper)
  • Client- und Server-Code in F# schreiben (Code Reuse) und automatisch zu .NET für Server und JavaScript für Client compilieren (mit WebSharper)
  • IntelliSense in F# für JavaScript Library Funktionen dank automatischer TypeScript Definitions File Auswertung.


Zu F# Inhouse Schulungskurse

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