Multiple items
val char : char
--------------------
type char = System.Char
Full name: Microsoft.FSharp.Core.char
val zero : int
namespace System
type Convert =
static val DBNull : obj
static member ChangeType : value:obj * typeCode:TypeCode -> obj + 3 overloads
static member FromBase64CharArray : inArray:char[] * offset:int * length:int -> byte[]
static member FromBase64String : s:string -> byte[]
static member GetTypeCode : value:obj -> TypeCode
static member IsDBNull : value:obj -> bool
static member ToBase64CharArray : inArray:byte[] * offsetIn:int * length:int * outArray:char[] * offsetOut:int -> int + 1 overload
static member ToBase64String : inArray:byte[] -> string + 3 overloads
static member ToBoolean : value:obj -> bool + 17 overloads
static member ToByte : value:obj -> byte + 18 overloads
...
Full name: System.Convert
System.Convert.ToInt32(value: System.DateTime) : int
(+0 other overloads)
System.Convert.ToInt32(value: string) : int
(+0 other overloads)
System.Convert.ToInt32(value: decimal) : int
(+0 other overloads)
System.Convert.ToInt32(value: float) : int
(+0 other overloads)
System.Convert.ToInt32(value: float32) : int
(+0 other overloads)
System.Convert.ToInt32(value: uint64) : int
(+0 other overloads)
System.Convert.ToInt32(value: int64) : int
(+0 other overloads)
System.Convert.ToInt32(value: int) : int
(+0 other overloads)
System.Convert.ToInt32(value: uint32) : int
(+0 other overloads)
System.Convert.ToInt32(value: uint16) : int
(+0 other overloads)
type Char =
struct
member CompareTo : value:obj -> int + 1 overload
member Equals : obj:obj -> bool + 1 overload
member GetHashCode : unit -> int
member GetTypeCode : unit -> TypeCode
member ToString : unit -> string + 1 overload
static val MaxValue : char
static val MinValue : char
static member ConvertFromUtf32 : utf32:int -> string
static member ConvertToUtf32 : highSurrogate:char * lowSurrogate:char -> int + 1 overload
static member GetNumericValue : c:char -> float + 1 overload
...
end
Full name: System.Char
System.Char.IsDigit(c: char) : bool
System.Char.IsDigit(s: string, index: int) : bool
union case Option.Some: Value: 'T -> Option<'T>
union case Option.None: Option<'T>
val parseScore : chars:char list -> Option<int> list
Full name: Index.parseScore
val chars : char list
type 'T list = List<'T>
Full name: Microsoft.FSharp.Collections.list<_>
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
module Option
from Microsoft.FSharp.Core
Multiple items
val int : value:'T -> int (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.int
--------------------
type int = int32
Full name: Microsoft.FSharp.Core.int
--------------------
type int<'Measure> = int
Full name: Microsoft.FSharp.Core.int<_>
val rest : char list
active recognizer Digit: char -> int option
Full name: Index.( |Digit|_| )
val x : int
val parseScoreResult : Option<int> list
Full name: Index.parseScoreResult
val countScore : scores:int list -> int
Full name: Index.countScore
val scores : int list
val count : (int -> int list -> int)
val frame : int
val b1 : int
val b2 : int
val next : int list
val r1 : int
val r2 : int
val countScoreResult : int
Full name: Index.countScoreResult
val sequence : optionals:'a option list -> 'a list option
Full name: Index.sequence
val optionals : 'a option list
type 'T option = Option<'T>
Full name: Microsoft.FSharp.Core.option<_>
val sequence' : ('b list option -> 'b option list -> 'b list option)
val acc : 'b list option
val optionals : 'b option list
val map : mapping:('T -> 'U) -> option:'T option -> 'U option
Full name: Microsoft.FSharp.Core.Option.map
Multiple items
module List
from Microsoft.FSharp.Collections
--------------------
type List<'T> =
| ( [] )
| ( :: ) of Head: 'T * Tail: 'T list
interface IEnumerable
interface IEnumerable<'T>
member GetSlice : startIndex:int option * endIndex:int option -> 'T list
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 rev : list:'T list -> 'T list
Full name: Microsoft.FSharp.Collections.List.rev
val h : 'b
val t : 'b option list
val acc : 'b list
val oneOption : string list option
Full name: Index.oneOption
val bowlingScore : score:string -> Option<int>
Full name: Index.bowlingScore
val score : string
Multiple items
val string : value:'T -> string
Full name: Microsoft.FSharp.Core.Operators.string
--------------------
type string = System.String
Full name: Microsoft.FSharp.Core.string
System.String.ToCharArray() : char []
System.String.ToCharArray(startIndex: int, length: int) : char []
module Array
from Microsoft.FSharp.Collections
val toList : array:'T [] -> 'T list
Full name: Microsoft.FSharp.Collections.Array.toList
val bowlingScoreResult : Option<int>
Full name: Index.bowlingScoreResult
Multiple items
type EntryPointAttribute =
inherit Attribute
new : unit -> EntryPointAttribute
Full name: Microsoft.FSharp.Core.EntryPointAttribute
--------------------
new : unit -> EntryPointAttribute
val printfn : format:Printf.TextWriterFormat<'T> -> 'T
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn
type byref<'T> = (# "<Common IL Type Omitted>" #)
Full name: Microsoft.FSharp.Core.byref<_>
type bool = System.Boolean
Full name: Microsoft.FSharp.Core.bool
type Int32 =
struct
member CompareTo : value:obj -> int + 1 overload
member Equals : obj:obj -> bool + 1 overload
member GetHashCode : unit -> int
member GetTypeCode : unit -> TypeCode
member ToString : unit -> string + 3 overloads
static val MaxValue : int
static val MinValue : int
static member Parse : s:string -> int + 3 overloads
static member TryParse : s:string * result:int -> bool + 1 overload
end
Full name: System.Int32
System.Int32.TryParse(s: string, result: byref<int>) : bool
System.Int32.TryParse(s: string, style: System.Globalization.NumberStyles, provider: System.IFormatProvider, result: byref<int>) : bool
val dict : keyValuePairs:seq<'Key * 'Value> -> System.Collections.Generic.IDictionary<'Key,'Value> (requires equality)
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.dict
namespace System.Collections
namespace System.Collections.Generic
Multiple items
type Dictionary<'TKey,'TValue> =
new : unit -> Dictionary<'TKey, 'TValue> + 5 overloads
member Add : key:'TKey * value:'TValue -> unit
member Clear : unit -> unit
member Comparer : IEqualityComparer<'TKey>
member ContainsKey : key:'TKey -> bool
member ContainsValue : value:'TValue -> bool
member Count : int
member GetEnumerator : unit -> Enumerator<'TKey, 'TValue>
member GetObjectData : info:SerializationInfo * context:StreamingContext -> unit
member Item : 'TKey -> 'TValue with get, set
...
nested type Enumerator
nested type KeyCollection
nested type ValueCollection
Full name: System.Collections.Generic.Dictionary<_,_>
--------------------
System.Collections.Generic.Dictionary() : unit
System.Collections.Generic.Dictionary(capacity: int) : unit
System.Collections.Generic.Dictionary(comparer: System.Collections.Generic.IEqualityComparer<'TKey>) : unit
System.Collections.Generic.Dictionary(dictionary: System.Collections.Generic.IDictionary<'TKey,'TValue>) : unit
System.Collections.Generic.Dictionary(capacity: int, comparer: System.Collections.Generic.IEqualityComparer<'TKey>) : unit
System.Collections.Generic.Dictionary(dictionary: System.Collections.Generic.IDictionary<'TKey,'TValue>, comparer: System.Collections.Generic.IEqualityComparer<'TKey>) : unit
type IDisposable =
member Dispose : unit -> unit
Full name: System.IDisposable
F# CAMP
Writing .NET applications in F#
- Open up new instance of Visual Studio 2015
- Make sure you have F# templates (in
Other Languages
)
- Let's stick to .NET Framework v4.5.1
Agenda
- F# Library (bowling score)
- F# Console app
- F# Build script - FAKE
- F# Test project - xUnit
- C# Window app - WPF (integration with F#)
- F# Web app
F# Library (bowling score)
- Create new, blank solution called "bowling" in directory of your choice
- Create new F# library called "bowling" in the solution
- Delete
Script.fsx
from the project
- Rename
Library1.fs
to Bowling.fs
- Open renamed file
Bowling.fs
in editor
- Remove generated code from the file, and insert module declaration:
! Remember to save all changes when manipulating projects in Visual Studio (Ctrl + Shift + S)
- Copy code for
Digit
active pattern recognizer into Bowling
module
1:
2:
3:
4:
5:
6:
|
let (|Digit|_|) char =
let zero = System.Convert.ToInt32 '0'
if System.Char.IsDigit char then
Some (System.Convert.ToInt32 char - zero)
else
None
|
- Copy code for
parseScore
function after Digit
1:
2:
3:
4:
5:
6:
7:
8:
9:
|
let rec parseScore (chars: list<char>) : list<Option<int>> =
match chars with
| [] -> []
| 'X' :: rest -> Some 10 :: parseScore rest
| Digit x :: '/' :: rest -> Some x :: Some (10 - x) :: parseScore rest
| Digit x :: rest -> Some x :: parseScore rest
| '-' :: '/' :: rest -> Some 0 :: Some 10 :: parseScore rest
| '-' :: rest -> Some 0 :: parseScore rest
| _ :: rest -> None :: parseScore rest
|
Test the code in Interactive
- Select all lines excluding module declaration
- Trigger Execute in Interactive
- In interactive window, enter following:
1:
|
let parseScoreResult = parseScore ['4'; '/'];;
|
Value of parseScoreResult
Exercise
- Extend
Digit
active pattern to recognize '-' character as well,
- Rename
Digit
to Pins
to better reflect its intent after the change,
- Refactor
parseScore
function - make use of the fact that Pins
recognizes now '-' and remove redundant pattern matching case(s),
- In interactive, make sure that after refactoring the code still works.
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
|
let countScore (scores: list<int>) : int =
let rec count frame scores =
match scores with
| [] -> 0
| 10 :: (b1 :: b2 :: _ as next) ->
10 + b1 + b2 + (if frame = 10 then 0 else count (frame+1) next)
| r1 :: r2 :: (b1 :: _ as next) when r1 + r2 = 10 ->
10 + b1 + (if frame = 10 then 0 else count (frame+1) next)
| r1 :: r2 :: next ->
r1 + r2 + count (frame+1) next
count 1 scores
|
- Test the function in interactive:
1:
|
let countScoreResult = countScore [10;9;1;5;5;7;2;10;10;10;9;0;8;2;9;1;10];;
|
Value of countScoreResult
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
|
let sequence (optionals: list<option<'a>>) : option<list<'a>> =
let rec sequence' acc optionals =
match optionals, acc with
| [],_ ->
Option.map List.rev acc
| Some h :: t, Some acc ->
sequence' (Some (h :: acc)) t
| _ ->
None
sequence' (Some []) optionals
|
- Test the function in interactive:
1:
|
let oneOption = sequence [Some "abc"; Some "def"; Some "ghi"];;
|
Value of oneOption
Some ["abc"; "def"; "ghi"]
|
- Add
bowlingScore
function
1:
2:
3:
4:
5:
6:
|
let bowlingScore (score: string) : Option<int> =
score.ToCharArray()
|> Array.toList
|> parseScore
|> sequence
|> Option.map countScore
|
- Test the function in interactive:
1:
|
let bowlingScoreResult = bowlingScore "X9/5/72XXX9-8/9/X";;
|
Value of bowlingScoreResult
Summary
- Creating F# Library projects in VS
- Declaring Bowling module
- Testing code in interactive
F# Console app
- Create new F# Console Application "bowling.console"
- Add project reference from "bowling.console" to "bowling"
- Compile "bowling" project
- Invoke
Bowling.bowlingScore
on example input and print output
1:
2:
3:
4:
5:
6:
7:
|
// Learn more about F# at http://fsharp.org
// See the 'F# Tutorial' project for more help.
[<EntryPoint>]
let main argv =
printfn "%A" (Bowling.bowlingScore "XXXXXXXXXXXX")
0 // return an integer exit code
|
Exercise
Invoke Bowling.bowlingScore
for each argument from argv
(console arguments)
XXXXXXXXXXXXX 9-9-9-9-9-9-9-9-9-9- 5/5/5/5/5/5/5/5/5/5/5 X9/5/72XXX9-8/9/X
Hint: Use Array.iter
function to perform an action for each element from an array
Summary
- Creating F# console apps
- Printing to console
F# Build script - FAKE
Paket for managing dependencies
- Create new directory ".paket" next to the ".sln" solution file
- Download paket.bootstrapper.exe from here and save it in ".paket" directory
- In console, change directory to where the solution file and ".paket" folder are located. Do not change directory to ".paket"
- Run paket.bootstrapper.exe from console to download newest Paket, and then invoke
paket.exe init
:
1:
2:
|
> .paket\paket.bootrapper.exe
> .paket\paket.exe init
|
- In solution, add "New Solution Folder" called ".project"
- "Add Existing Item" - add "paket.dependencies" file to the ".project" solution folder
- Open "paket.dependencies" file in the VS editor
Modify the "paket.dependencies" file to add "Build" group and "FAKE" package:
1:
2:
3:
4:
|
group Build
source https://www.nuget.org/api/v2
nuget FAKE
|
Run paket install:
1:
|
> .paket\paket.exe install
|
Add "New Item", "build.cmd" to the ".project" solution folder:
1:
2:
3:
4:
5:
|
@echo off
cls
.paket\paket.bootstrapper.exe
.paket\paket.exe restore
packages\Build\FAKE\tools\FAKE.exe build.fsx %*
|
Add "New Item", "build.fsx" to the ".project" solution folder:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
|
#r @"packages/Build/FAKE/tools/FakeLib.dll"
open Fake
Target "Build" (fun _ ->
MSBuildRelease "bin" "Build" ["bowling.sln"]
|> Log "Build output"
)
RunTargetOrDefault "Build"
|
Run the build script:
Summary
- Paket for managing dependencies
- FAKE for build scripts
- Invoking build script from command line
- Create new F# Library "bowling.tests" for .NET 4.5.1,
- Remove "Script.fsx" file,
- Rename "Library1.fs" to "Tests.fs",
- "Add new item", "App.config" application configuration file to "bowling.tests",
- Add Project Reference from "bowling.tests" to "bowling",
- Remove boilerplate code and declare
Bowling.Tests
module:
! Save all changes in Visual Studio
- Open "paket.dependencies" in VS editor,
- Add "xunit.runner.console" package to "Build" group,
- Add new group "Tests" with "framework: net451",
- Add "FSharp.Core" 4.0.0.1 with "redirects: force" option, "xUnit" and "FsUnit.xUnit" nugets to "Tests" group
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
|
group Build
source https://www.nuget.org/api/v2
nuget FAKE
nuget xunit.runner.console
group Tests
framework: net451
source https://www.nuget.org/api/v2
nuget FSharp.Core 4.0.0.1 redirects: force
nuget xUnit
nuget FsUnit.xUnit
|
- Add "New Item", "paket.references" (General -> Text File) to "bowling.tests" project
- Open "paket.references" file in VS editor and fill it with below:
1:
2:
3:
4:
|
group Tests
FSharp.Core
xUnit
FsUnit.xUnit
|
Run "paket install":
1:
|
> .paket\paket.exe install
|
- Open "build.fsx" build script in VS editor,
- Add "Tests" build target:
1:
2:
3:
4:
5:
6:
7:
8:
9:
|
open Fake.Testing // for testing helper functions
Target "Tests" (fun _ ->
["bin/bowling.tests.dll"]
|> xUnit2 (fun xunitParams ->
{ xunitParams with ToolPath = @"packages/Build/xunit.runner.console/"
+ @"tools/xunit.console.exe" }
)
)
|
At the bottom of "build.fsx", specify Target dependency and change default target to "Tests":
1:
2:
3:
4:
5:
6:
|
// Targets above
"Build"
==> "Tests"
RunTargetOrDefault "Tests"
|
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
|
#r @"packages/Build/FAKE/tools/FakeLib.dll"
open Fake
open Fake.Testing
Target "Build" (fun _ ->
MSBuildRelease "bin" "Build" ["bowling.sln"]
|> Log "Build output"
)
Target "Tests" (fun _ ->
["bin/bowling.tests.dll"]
|> xUnit2 (fun xunitParams ->
{ xunitParams with ToolPath = @"packages/Build/xunit.runner.console/"
+ @"tools/xunit.console.exe" }
)
)
"Build"
==> "Tests"
RunTargetOrDefault "Tests"
|
- Open "Tests.fs" source file in editor,
- Add unit test for checking score of 12 Strikes:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
|
module Bowling.Tests
open Xunit
open FsUnit.Xunit
[<Fact>]
let ``12 strikes in row`` () =
let expected = Some 270
let actual = Bowling.bowlingScore "XXXXXXXXXXXX"
actual |> should equal expected
|
Run the build script (without any additional parameters):
Exercises
- Fix the test
-
Add three more test cases for following scores:
- "9-9-9-9-9-9-9-9-9-9-"
- "5/5/5/5/5/5/5/5/5/5/5"
- "X9/5/72XXX9-8/9/X"
Summary
- Creating test library in F#
- Adding test nuget packages with Paket
- Attaching tests to the build script pipeline
- Writing unit tests in F#
C# Window app - WPF (integration with F#)
- Add C# Windows "WPF Application" project, "bowling.wpf" to the solution,
- Add project reference from "bowling.wpf" to "bowling",
- Design awesome GUI with a TextBox, TextBlock and a Button:
- Open "paket.dependencies" file,
- Add "FSharp.Core" 4.0.0.1 package with "redirects: force" option to main group,
- Use "framework: net451" for main group as well:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
|
framework: net451
source https://www.nuget.org/api/v2
nuget FSharp.Core 4.0.0.1 redirects: force
group Build
source https://www.nuget.org/api/v2
nuget FAKE
nuget xunit.runner.console
group Tests
framework: net451
source https://www.nuget.org/api/v2
nuget FSharp.Core 4.0.0.1 redirects: force
nuget xUnit
nuget FsUnit.xUnit
|
Add "New Item", "paket.references" to "bowling.wpf":
Run paket install:
1:
|
> .paket\paket.exe install
|
Add action on button click:
1:
2:
3:
4:
5:
6:
7:
8:
9:
|
private void button_Click(object sender, RoutedEventArgs e)
{
var input = textBox.Text;
var score = Bowling.bowlingScore(input);
textBlock.Text =
FSharpOption<int>.get_IsSome(score) ?
"Score: " + score.Value.ToString() :
"Wrong score!";
}
|
Run it!
The F# Component Design Guidelines
Below snippet doesn't feel nice in C#:
1:
2:
3:
4:
|
textBlock.Text =
FSharpOption<int>.get_IsSome(score) ?
"Score: " + score.Value.ToString() :
"Wrong score!";
|
Exercise
Create new function TryGetBowlingScore
in Bowling
module for better interop with C#, conforming to the F# Component Design Guidelines.
Use the new function in code behind button click in C#.
Skeleton of the function
1:
2:
3:
|
let TryGetBowlingScore(score: string, result : byref<int>) : bool =
result <- 0
false
|
New stuff - assign value operator
1:
2:
|
let mutable x = 0
x <- 5
|
Using .NET libraries from F#
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
|
let (i1success,i1) = System.Int32.TryParse("123");
if i1success then printfn "parsed as %i" i1 else printfn "parse failed"
let dict = new System.Collections.Generic.Dictionary<string,string>()
dict.Add("a","hello")
let (e1success,e1) = dict.TryGetValue("a")
let (e2success,e2) = dict.TryGetValue("b")
let makeResource name =
{ new System.IDisposable
with member this.Dispose() = printfn "%s disposed" name }
|
https://fsharpforfunandprofit.com/posts/completeness-seamless-dotnet-interop/
Summary
- Referencing F# code from C# (FSharp.Core package)
- Conforming to the F# Component Design Guidelines
- Using .NET libraries from F#
- Add F# Console application project, "bowling.web" to the solution,
- Add project reference from "bowling.web" to "bowling",
- Open "paket.dependencies" file,
- Add "Suave" package to main group:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
|
framework: net451
source https://www.nuget.org/api/v2
nuget FSharp.Core 4.0.0.1 redirects: force
nuget Suave
group Build
source https://www.nuget.org/api/v2
nuget FAKE
nuget xunit.runner.console
group Tests
framework: net451
source https://www.nuget.org/api/v2
nuget FSharp.Core 4.0.0.1 redirects: force
nuget xUnit
nuget FsUnit.xUnit
|
Add "New Item", "paket.references" to "bowling.web":
Run paket install:
1:
|
> .paket\paket.exe install
|
- Open "Program.fs" from "bowling.web",
- Remove boilerplater code, and insert following hello world suave:
1:
2:
3:
|
open Suave
startWebServer defaultConfig (Successful.OK "Hello world")
|
Run it!
Exercise
Implement scoreHandler
function so that:
- it responds with 200 OK with score for correct input,
- it responds with 400 BAD REQUEST with "Wrong result" message for wrong input:
1:
2:
3:
4:
5:
6:
|
open Suave
let scoreHandler (input: string) : WebPart =
Successful.OK "Hello world"
startWebServer defaultConfig (Filters.pathScan "/%s" scoreHandler)
|
Hint: Make use of Successful.OK
and RequestErrors.BAD_REQUEST
functions. Both are of type string -> WebPart
.
Demo: .fs files order in project
(order matters)
At first it looks like a limitation but it really turns out to be one of the most beloved F# features
Summary
- Suave.IO is a very light-weight server library, easy to use with F#
- .fs file order inside project matters
Summary
- F# Library (bowling score)
- F# Console app
- F# Build script - FAKE
- F# Test project - xUnit
- C# Window app - WPF (integration with F#)
- F# Web app