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’

Warum F#

  Mit über 30 Jahren Erfahrung in der Programmierung und Softwareentwicklung habe ich sehr vieles gesehen, gemacht und gelernt. Jahrzehnte lang war die Objektorientierte Entwicklung das A und O. Aber der Fortschritt stockte doch irgendwie, wenn man Software mit Elektronik vergleicht. Software Entwicklung dauerte typischerweise immer länger als geplant. Die Software Qualität von Fehlverhalten bis hin zu Anwenderfreundlichkeit lässt immer noch zu wünschen übrig. Die Aufwände trotz der viel gelobten Wiederverwendung von Objektorientiertem Code wurden nie kleiner.
Das kann doch nicht sein. Wo ist der Fortschritt geblieben? Stecken wir in einer Sackgasse?
Wie in “Über mich" beschrieben, ich suchte und fand etwas in Vergessenheit geratenes, die Funktionale Programmierung (FP), die schon älter ist als die Objektorientierte.
Und ich glaube es ist der Schlüssel, vieles einfach und besser zu machen und aus dem Sackgassen Dilemma auszubrechen und die Produktivität und Qualität von Software massiv zu erhöhen.
Der Grund wieso man jetzt immer öfter von FP hört ist, weil es die Beste Lösung für die Skalierbarkeit der immer wachsenden Prozessor Anzahl in Computern ist. Also die Asynchrone- und Parallele-Programmierung. So dass die Software die vorhandenen Hardware Ressourcen korrekt und effizient Nutzen kann und so dem Anwender das bieten was er erwartet. Meine Erfahrung zeigt mir, die praxisorientierte Programmiersprache dazu mit sofortigem praktischem Nutzen und ohne viel verwirrende Sprachkonstrukte, ist F# für .NET.
  • Voll kompatibel mit der kompletten .NET Umgebung.
  • Und es gibt Sicherheit und ist durchschaubar, denn F# ist Open Source.
  • Baut auf lange bewährtem auf und ist keine reine Microsoft Erfindung.
Der Stammbaum : Die Abstammung von F#
ML (1973) -> SML (1984) -> Caml (1987) -> OCaml (1996) – – -> F# (2007)
F# ist sehr stark an OCaml orientiert und z.T. auch Syntax kompatibel, also OCaml ist CAML mit objektorientiertem Zusatz, der wohl im OO-Hype Zeitalter dazu genommen wurde. Quelle: Programmiersprachen Stammbaum Die anderen aktuellen FP Sprachen und ihre Einflüsse sind z.B.
  • F# 1.0 (2007) : Objective CAML, C#, Haskell
  • Scala 1.0 (2003) : Java, Pizza, ML, Haskell, Smalltalk, Erlang
  • Haskell 1.0 (1987) : APL, LISP, Miranda, ML
Quelle: Wikipedia Was Entwickler überzeugen wird folgt Stück für Stück in technischen Blogs.

Über mich

Mein Name ist Roman Bossart, in diesem Blog hier schreibe ich über Software Entwicklung mit Funktionaler Programmierung.

Ich bin Gründer und Inhaber von Bossart Analytics – Calibration Model und beschäftige mich mit Machine Learning und Software Entwicklung und lebe in der Schweiz nahe St. Gallen.

Unter folgenden Links finden Sie weitere Informationen:
Kontakt
Impressum
Twitter
StackOverflow


Warum blogge ich

Ich schreibe dies einerseits für mich als Zusammenfassung und als Dank an die motivierende innovative F# Community und für alle die, die an der neuen “Software Revolution” vorne dabei sein und teilnehmen wollen. Ich glaube die Softwareentwicklung macht nun endlich nach Jahrzehnten einen grossen Schritt in die richtige Richtung. Wir brechen aus, aus der OO-dominierten Sackgasse. Viele Sprachen haben funktionale Aspekte aufgenommen. Wer dies gut findet, der findet und erkennt noch viel mehr Möglichkeiten und Denkansätze, wenn er eine reine Sprache einsetzt, die ihre Funktionalen Wurzeln im Stammbaum seit 1973 weiterpflegt. Ich habe es erlebt und möchte diese Erfahrung und Wissen mit euch teilen.

Viele bloggen während sie etwas lernen, mit der Motivation ihre neuen Teil-Erkenntnisse stückweise zu dokumentieren. Dieser Blog ist eine Zusammenfassung von mir, der viele Teil-Lern-Puzzles schon hinter sich hat. Dies ergibt den Vorteil keine Abwege und Sackgassen-Erkenntnisse zu enthalten. Ein abgestimmtes Gesamtbild das sich auf die wichtigsten Fakten konzentriert und alle Kernpunkte und deren Essenz beleuchtet alles ganz pragmatisch und praxistauglich.

Das schöne ist, wenn man (fast) von Anfang an dabei ist und beobachtet wie das Aufkommen und Ausbreiten der F# Sprache, deren Anwender, Anwendungen und Tools, Bibliotheken und Open-Frameworks voranschreitet, dass man alles stückweise mitbekommt und die Zeit hat alles was neu ist zu lesen und auszuprobieren. Somit hat man einen nahezu kompletten Überblick was es gibt. Startet man heute ist so vieles schon da und man weiß nicht recht womit man starten soll. Also kein systematischer Aufbau, kein roter Faden den man folgen kann und so ohne Umweg die neue Welt von F# entdecken kann.

Dies will ich hier tun und beschreibe den geradlinigen Weg, der die typischen Problempunkte schon zu beginn anspricht und klarstellt, so dass der Einstieg und Anwendung glatt und scharf# geschliffen ohne Unfälle von statten geht.


Wer schreibt hier?

Damit Ihr wisst, von wem die Texte und Tipps stammen, wie ich geprägt bin, und wie und warum ich F# gefunden habe und nicht mehr loslasse.

Seit Ende der 80-er Jahre entwickle ich Software objektorientiert (ADA, PASCAL, C++, C#) und 2009 habe habe ich F# für mich entdeckt.

Es kam so, ich war 2008 von LINQ (mit C#) so begeistert, dass ich mich eines Tages fragte woher es kommt. Dabei bin ich auf Microsoft Research gestossen, aus dieser Schmiede kam es. Und was noch viel interessanter war, diese Leute arbeiten an einer neuen Sprache, es war Microsoft F#, May 2009 Community Technology Preview die man sich zum Visual-Studio 2010 Beta dazu installieren konnte. Meine Begeisterung steigerte sich mit F#, denn durch das interaktive Ausprobieren, konnte man schnell Vorschritte beim Erlernen machen. Nicht erst ein Testprogramm schreiben zu müssen, um seine Code Fragmente auszuprobieren. Und es kam noch besser, es ist ja nicht nur die Sprache selbst, es ist auch das ganze .Net Framework, dass man so auch interaktiv Ausprobieren kann. Dies war früher nie möglich in C#, heute mit LINQPad und Konsorten geht das ja.

Ich kann so heute ganz glücklich auf mehrere produktive Jahre Erfahrung mit exklusiver F# Entwicklung zurückblicken.


F#, d.h. die Funktionale Programmierung, die ich vom Prinzip und Anwendung bereits seit 1984 von LISP kannte, konnte ich nun auch auf das .NET Framework anwenden, das ich schon seit 2002 (.NET 1.0) kenne. Im Gegensatz zu LISP macht F# nicht soviel Klammerungen nötig, was mir ganz gut gefällt.

Und das aller Beste an F# ist, man hat FP + OOP + .NET + interaktiv, in Script-Form und in kompilierter Form. Der Code ist kompakt, leserlich, wenig fehleranfällig und beschränkt sich auf das was man tun will. Und dies alles auch noch als Open Source (ja der komplette Compiler! Spaßeshalber von jemandem einmal als FOSLYN bezeichnet mit Anspielung auf ROSLYN) auf allen wesentlichen Plattformen wie Windows, Mac, Linux, Android, iOS (iPhone/iPad), JS/HTML5, GPU, FreeBSD.


Pro-Tip : Also wenn ihr auf LINQ, LINQPad oder PowerShell nicht verzichten könnt, dann müsst ihr unbedingt (zwangsmäßig) F# ausprobieren! Denn da kommt alles und noch viel mehr zusammen und alles ist aus einem Guss. Ich schreibe das hier, weil niemand später sagen soll, “Das hätte man mir doch früher sagen sollen! Da hätte ich mir viel Zeit und Mühe gespart.” So ich habe es gesagt / geschrieben.

2014 das Jahr der Software Fehler und Sicherheitslecks?

Das Jahr 2014 beginnt mit der Aufdeckung der schlimmsten GOTO Fehlern in Betriebssystemen.

25. Februar : goto fail : Apple iOS, OS X :  SSL-Sicherheitsleck

Diese Lücke besteht angeblich seit 2012.
if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0) goto fail; goto fail;
Quellen: apple-mac-update-go-fail-ssl-bug , new-ios-flaw , gotofail.com

4. März : goto cleanup : Linux : “GnuTLS bug”

Diese Lücke besteht angeblich seit 2005 ! corrected return codes - Gitorious_2014-03-05_15-18-32 Expertenmeinungen dazu:
It looks pretty terrible. — Matt Green, Johns Hopkins University professor cryptography
has a lot of side effects. — Kenneth White, principal security engineer
Quellen: Sicherheitsluecke-GnuTLS , critical-crypto-bug-linux

Wie lange werden wir noch mit Software Fehlern leben müssen?

Es sind massive Sicherheitsrisiken! Software steckt in vielen Geräten. Anwender und Hersteller sind betroffen. Die von genervten Anwendern, die eine andere Software oder Geräte vorziehen, bis zu Daten-, Geld- und Imageverlust reichen können. Man denke auch an Bereiche der Personensicherheit und Medizintechnik, wo es auch um Menschenleben gehen kann! Wie lange werden wir auch noch mit einer anderen Fehlerquelle der NULL Ausnahmen (Null Pointer Exception, Null Reference Exception) leben müssen?
“I call it my billion-dollar mistake. It was the invention of the null reference in 1965.” — Tony Hoare
Diese Problematik betrifft nahezu alle heute noch verwendeten älteren Programmiersprachen wie z.B. C, C++, Java, VB.Net, C# ! Der Grund ist der Stammbaum, also die Abstammung der Programmiersprachen und deren weitergegebenen Konzepte. Quellen: Null PointerZeiger

Was können wir dagegen tun?

Wie kann man die Software Qualität in seinen Wurzeln verbessern? Indem man für die Softwareentwicklung eine Programmiersprache anwendet, die weder GOTO noch NULL benötigt, die kapitalen Fehler nicht zulässt und dabei den Entwickler aktiv und wachsam unterstützt, so z.B. die immer populärer werdende Programmiersprache F#, die hier auf Functional Software .NET das Thema ist.   Nebenbei bemerkt, wer eine TLS Implementation in F# betrachten will, kann dies hier tun “miTLS A verified reference TLS implementation” und es ist auf den Tag genau ein Jahr her, hier gibt es den F# Quellcode: “5 March 2013 Our paper on TLS security has been accepted at the IEEE Symposium on Security & Privacy. A draft technical report of this work is available from the download page.”. Siehe auch F7: Refinement Types for F#