F# syntax highlighting dark-theme coloring scheme for VS-2015 with Visual F# Power Tools

Here I share my customized Visual-Studio 2015 (VS-2015) dark-theme coloring scheme that works with Visual F# Power Tools (VFPT).

The selection of colors for syntax highlighting is nearly as tricky as finding good names for functions and values. And it depends on personal taste.

For this dark color scheme the brighter/colorful colors are chosen to highlight the domain relevant things like the names for
  • modules,
  • types (Discriminated Unions (DU), records, classes),
  • Unit of Measures (UoM),
  • values and
  • parameters
The F# language specific syntax elements are defined in darker color. That allows to better focus on the domain during code reading and writing.

This is an example of F# code highlighted with this coloring scheme.
F#-Rewrite-of-refactored-c#
The settings can be found under VS > Tools > Options > Environment > Fonts and Colors > F# … Font-and-Colors-for-F#
The screenshot shows the VS-2015 Options dialog where F# Mutable Variables and Reference Cells are defined in red as a warning (which is good default).

Note that by default not all F# Display items are defined in a stand out color.
Note that the  Visual F# Power Tools (VFPT) must be installed first.
Note that in VS > Tools > Options > F# Power Tools the Syntax coloring is activated.
Visual F# Power Tools Settings

Note: don’t forget to make a backup of your Visual Studio settings before importing and changing settings.
Here is an zipped export (*.vssettings) of the VS-2015-FSharp-Font-and-Colors-Exported-and-pretty-printed-XML containing only font and colors.


And the big thank you goes to the F# community for this excellent tool which is open source.





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

F# Videos 2015

Eine sehr gute Staffel von Videos zu F#, kurz und auf dem Punkt. 
Von Tomas Petricek und Seth Juarez von Channel9. Erschienen am 6.6.2015.

1. F# für Manager und CTOs
Time-to-Market (TTM),  Korrektheit, komplexe Probleme lösen, effiziente skalierbare Software

2. F# für Architekten und Domain Experten
Domain Driven Development (DDD)

3. F# für Web Entwickler und Data Scientists
Eleganter Zugriff auf Web-Services mit minimalem Code und ohne Tools.

4. F# für Web Entwickler
Eine WebApp in unter 50 Zeilen Code mit Azure Deployment.


Wer mit .NET entwickelt sieht sich am besten die ganze F# Video Staffel (1-4) an, es ist empfehlenswert!

1. Für Manager
“Making the Case for using F#”


2. Für Architekten und Domain Experts
“Domain Modeling in F#”


3. Für Web Entwickler und Data Scientists
“Type Providers in F#”


4. Für Web Entwickler
“Deploying an F# Web Application with Suave”


Weiterführende Information zu Suave gibt es hier :
– Eine komplette Beschreibung zu einer “Music Store” Suave WebApp https://www.gitbook.com/book/theimowski/suave-music-store/details
– Suave Web Development F# Library https://github.com/SuaveIO/suave
– Suave Homepage : Suave is a lightweight, non-blocking web server http://suave.io/

Documenting the available .NET standard formats of .ToString(fmt) with a small F# code snippet

Inspired by ploeh’s stringf

Listing all standard formats of .ToString for DateTime with

1: 
let dateFormats = docuPrint DateTime.Now

that results in the following, where you can pick your favorite format easily.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
('M', "08 Mai")
('m', "08 Mai")
('d', "08.05.2015")
('g', "08.05.2015 19:03")
('G', "08.05.2015 19:03:24")
('t', "19:03")
('T', "19:03:24")
('u', "2015-05-08 19:03:24Z")
('s', "2015-05-08T19:03:24")
('O', "2015-05-08T19:03:24.8516093+02:00")
('o', "2015-05-08T19:03:24.8516093+02:00")
('D', "Freitag, 8. Mai 2015")
('U', "Freitag, 8. Mai 2015 17:03:24")
('f', "Freitag, 8. Mai 2015 19:03")
('F', "Freitag, 8. Mai 2015 19:03:24")
('R', "Fri, 08 May 2015 19:03:24 GMT")
('r', "Fri, 08 May 2015 19:03:24 GMT")
('Y', "Mai 2015")
('y', "Mai 2015")

The main logic is this simple, we try all single char formats on the given type.
Yes it’s a generic function, because of the inline.

1: 
2: 
3: 
let inline getAllFormatSpecifier someType =
    ['A' .. 'Z'] @ ['a' .. 'z']  
    |> List.choose (exToOption (tupleTee (string >> flip stringf someType))) 

It’s signature look like this

1: 
2: 
3: 
val inline getAllFormatSpecifier :
  someType: ^a -> (char * string) list
    when  ^a : (member ToString :  ^a * string -> string)

Here is how to read this nice function composition from left to right:

  1. List.choose keeps only Some‘s
  2. The Some comes from exToOption that converts exceptions to None and successful results to Some
  3. tupleTee keeps the input of the function togehter with the result of the function as a tuple
  4. string converts a character from the concatinated lists ['A' .. 'Z'] @ ['a' .. 'z'] to a string to fit the stringf
  5. and finally the parameter order needs to be exchanged with flip and go into stringf with the given someType

The full code

  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: 
 52: 
 53: 
 54: 
 55: 
 56: 
 57: 
 58: 
 59: 
 60: 
 61: 
 62: 
 63: 
 64: 
 65: 
 66: 
 67: 
 68: 
 69: 
 70: 
 71: 
 72: 
 73: 
 74: 
 75: 
 76: 
 77: 
 78: 
 79: 
 80: 
 81: 
 82: 
 83: 
 84: 
 85: 
 86: 
 87: 
 88: 
 89: 
 90: 
 91: 
 92: 
 93: 
 94: 
 95: 
 96: 
 97: 
 98: 
 99: 
100: 
101: 
102: 
103: 
104: 
105: 
106: 
107: 
108: 
109: 
110: 
111: 
112: 
113: 
114: 
115: 
116: 
117: 
118: 
119: 
120: 
121: 
122: 
123: 
124: 
125: 
126: 
127: 
128: 
129: 
130: 
131: 
132: 
133: 
open System

// http://blog.ploeh.dk/2015/05/08/stringf/
let inline stringf format (x : ^a) = 
    (^a : (member ToString : string -> string) (x, format))

let flip f a b = f b a
let tupleTee f x = x, f x
let exToOption f x = try Some(f x) with _ -> None

// function composition in F# is awesome !
let inline getAllFormatSpecifier someType =
    ['A' .. 'Z'] @ ['a' .. 'z']  
    > List.choose (exToOption (tupleTee (string >> flip stringf someType))) 

// print it nice to choose one
let inline docuPrint x =
    x
    |> getAllFormatSpecifier
    |> List.sortBy snd              
    |> List.iter (printfn "%A")

let dateFormats = docuPrint DateTime.Now
(*
('M', "08 Mai")
('m', "08 Mai")
('d', "08.05.2015")
('g', "08.05.2015 19:03")
('G', "08.05.2015 19:03:24")
('t', "19:03")
('T', "19:03:24")
('u', "2015-05-08 19:03:24Z")
('s', "2015-05-08T19:03:24")
('O', "2015-05-08T19:03:24.8516093+02:00")
('o', "2015-05-08T19:03:24.8516093+02:00")
('D', "Freitag, 8. Mai 2015")
('U', "Freitag, 8. Mai 2015 17:03:24")
('f', "Freitag, 8. Mai 2015 19:03")
('F', "Freitag, 8. Mai 2015 19:03:24")
('R', "Fri, 08 May 2015 19:03:24 GMT")
('r', "Fri, 08 May 2015 19:03:24 GMT")
('Y', "Mai 2015")
('y', "Mai 2015")
*)

let timespanFormats = docuPrint <| TimeSpan(1, 2, 3)
(*
('T', "01:02:03")
('c', "01:02:03")
('t', "01:02:03")
('G', "0:01:02:03.0000000")
('g', "1:02:03")
*)

// be caereful, it rounds
let floatFormats = docuPrint 0.425      
(*
('G', "0.425")
('R', "0.425")
('g', "0.425")
('r', "0.425")
('F', "0.43")
('N', "0.43")
('f', "0.43")
('n', "0.43")
('E', "4.250000E-001")
('e', "4.250000e-001")
('P', "42.50%")
('p', "42.50%")
('C', "Fr. 0.43")
('c', "Fr. 0.43")
*)

// be caereful, it rounds
let decimalFormats = docuPrint 0.425m
(*
('G', "0.425")
('g', "0.425")
('F', "0.43")
('N', "0.43")
('f', "0.43")
('n', "0.43")
('E', "4.250000E-001")
('e', "4.250000e-001")
('P', "42.50%")
('p', "42.50%")
('C', "Fr. 0.43")
('c', "Fr. 0.43")
*)


let intFormats = docuPrint 42   
(*
('X', "2A")
('x', "2a")
('P', "4'200.00%")
('p', "4'200.00%")
('E', "4.200000E+001")
('e', "4.200000e+001")
('D', "42")
('G', "42")
('d', "42")
('g', "42")
('F', "42.00")
('N', "42.00")
('f', "42.00")
('n', "42.00")
('C', "Fr. 42.00")
('c', "Fr. 42.00")
*)


let bigIntFormats = docuPrint 42I
(*
('X', "2A")
('x', "2a")
('P', "4'200.00%")
('p', "4'200.00%")
('E', "4.200000E+001")
('e', "4.200000e+001")
('D', "42")
('G', "42")
('R', "42")
('d', "42")
('g', "42")
('r', "42")
('F', "42.00")
('N', "42.00")
('f', "42.00")
('n', "42.00")
('C', "Fr. 42.00")
('c', "Fr. 42.00")
*)
val dateFormats : obj

Full name: DocumentingToString.dateFormats
val getAllFormatSpecifier : someType:'a -> 'b list

Full name: DocumentingToString.getAllFormatSpecifier
val someType : 'a
Multiple items
module List

from 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 choose : chooser:('T -> 'U option) -> list:'T list -> 'U list

Full name: Microsoft.FSharp.Collections.List.choose
Multiple items
val string : value:'T -> string

Full name: Microsoft.FSharp.Core.Operators.string

——————–
type string = System.String

Full name: Microsoft.FSharp.Core.string
Multiple items
val char : value:'T -> char (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.char

——————–
type char = System.Char

Full name: Microsoft.FSharp.Core.char
type 'T list = List<'T>

Full name: Microsoft.FSharp.Collections.list<_>
namespace System
union case Option.Some: Value: 'T -> Option<'T>
union case Option.None: Option<'T>
val sortBy : projection:('T -> 'Key) -> list:'T list -> 'T list (requires comparison)

Full name: Microsoft.FSharp.Collections.List.sortBy
val snd : tuple:('T1 * 'T2) -> 'T2

Full name: Microsoft.FSharp.Core.Operators.snd
val iter : action:('T -> unit) -> list:'T list -> unit

Full name: Microsoft.FSharp.Collections.List.iter
val printfn : format:Printf.TextWriterFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn

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