Links
Command line
|
dotnet fsi # start F# Interactive
dotnet fsi script.fsx # run script.fsx
dotnet new console -o fsharp1 -lang f#
|
|
let s = "value"
printfn "text %A text" s
|
Format
|
Type
|
%A |
any
|
%s |
string
|
%i |
integer
|
%b |
boolean
|
for
|
for i in [1..3] do
printfn "%A" i
for i = 1 to 3 do
printfn "%A" i
|
while
|
let mutable keepRunning = true
while keepRunning do
// ...
keepRunning <- false
|
match
|
let x = 2
let result =
match x with
| 1 -> "one"
| 2 -> "two"
| 3 | 4 -> "soon implemented" // 3 or 4
| _ -> "not implemented"
| y -> $"not implemented for {y}"
// pattern matching function syntax
let numberToWord = function
| 1 -> "one"
| 2 -> "two"
| _ -> "not implemented"
numberToWord x // "two"
|
String
|
let s = "abcde"
// slice
s[1..3] // "bcd"
// triple-quote string allows to use double-quote within the string
let s = """<tag attribute="value" />"""
// concat strings
let sb = StringBuilder()
sb.Append("abc") |> ignore
sb.Append("def") |> ignore
|
Collection
List
Ordered, immutable, series of elements of the same type.
|
let list1 = [1; 2; 3]
let list2 = [1..5] // [1; 2; 3; 4; 5]
let list3 = [0..2..10] // [0; 2; 4; 6; 8; 10]
let list4 = [for x in 1..5 -> x*x] // [1; 4; 9; 16; 25]
// elements in lists are not mutable
// cons operator
0 :: list1 // create a new list with 0 at the head [0; 1; 2; 3]
// extract head and tail
let head :: tail = list1 // head: 1, tail: [2; 3]
// concatenate operator
list1 @ [4; 5] // create a new list [1; 2; 3; 4; 5]
List.sum(list1) // 6
List.fold (+) 0 list1 // 6 - aggregate
List.iter (fun x -> printfn "%A" x) list1 // 1 2 3
|
Array
Fixed-sized, zero-based, mutable collection of consecutive elements of the same type.
|
let myArray = [|1; 2; 3|]
// elements in arrays are mutable
myArray[1] <- 4 // [|1; 4; 3|]
|
Map
Set
Immutable Dictionary
Composite data type
Tuple
|
let tuple1 = (1, "one")
let (a, b) = tuple1 // a: 1, b: "one"
|
Record
|
type Item = { Id: int; Name: string; }
let item1 = { Id = 1; Name = "one" }
|
Function
|
let add x y = x + y
add 1 2 // 3
let myFunction param1 param2 =
let result = param1 + param2
result
// let myFunction (param1: int) (param2: int) : int = ...
myFunction 1 2 // 3
|
Unit
Function which doesn't return any value.
|
let myUnit (param1: int) (param2: int) =
let result = param1 + param2
printfn "%i" result
() // useless because printfn is already a unit
myUnit 1 2 // 3
|
Convertion
|
int "123" // 123
float 123 // 123.0
string 123 // "123"
|
Console input
|
printf "Enter a value: "
let input = Console.ReadLine()
printfn "Entered value: %s" input
|
.vscode/launch.json
|
{
"configurations": [
{
"console": "integratedTerminal" // // switch from internalConsole to integratedTerminal to allow console input in VS code
}
]
}
|
Use binding for disposable object
|
use file = File.CreateToText("file.txt")
// Dispose will be called when binding goes out of the scope
|
Module
Équivalent to a C# static class.
|
// nested module
module Math =
let add x y = x + y
let sub x y = x - y
let v = Math.add 1 2
open Math
let v = add 1 2
// top level module (no =, no indentation, in a separate file)
module Math
let add x y = x + y
let sub x y = x - y
|
Module Design Pattern
Separate the definition of data and the behaviors.
DomainTypes.fs
|
namespace Finance
module DomainTypes =
type Transaction =
{ Id: int; Amount: float}
|
Transaction.fs
|
namespace Finance
module Transaction =
let create id amount =
{ Id = id; Amount = amount }
|
Execute a shell command
Process.fs
|
module Process =
let Run command args =
let psi = Diagnostics.ProcessStartInfo()
psi.UseShellExecute <- false
psi.WindowStyle <- Diagnostics.ProcessWindowStyle.Hidden
psi.RedirectStandardOutput <- true
psi.RedirectStandardError <- true
let stdout = Event<string>()
let stderr = Event<string>()
psi.FileName <- command
psi.Arguments <- args
// psi.WorkingDirectory
let stdout = Event<string>()
let stderr = Event<string>()
let writeOut = printfn "%s"
stdout.Publish |> Event.add writeOut
stderr.Publish |> Event.add writeOut
use p = new Diagnostics.Process()
p.StartInfo <- psi
p.OutputDataReceived |> Event.add(fun ev -> if ev.Data <> null then stdout.Trigger ev.Data)
p.ErrorDataReceived.Add(fun ev -> if ev.Data <> null then stderr.Trigger ev.Data)
p.Start() |> ignore
p.BeginOutputReadLine()
p.BeginErrorReadLine()
p.WaitForExit()
p.ExitCode
|
Compilation order
MyProject.fsproj
|
<ItemGroup>
<Compile Include="File2.fs" />
<Compile Include="File1.fs" />
</ItemGroup>
|
Dev env
- VScode
- Ionide for F# extension → intellisense
- fantomas-fmt → format F#
Create launch and tasks
- Disable the Ionide for F# extension
- Run → Run Without Debugging
- Select environment: .NET 5+ and .NET Core (this create the launch.json file)
- Open launch.json → Add Configuration → .NET: Launch .NET Core Console App
- Fill target-framework = net6.0 and project-name = myproject
- Run → Run Without Debugging
- Configure Task → Create tasks.json file from template → Select a Task Template: .NET Core