Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix indentation in tutorial.fsx #568

Merged
merged 1 commit into from
Nov 19, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 71 additions & 68 deletions docsrc/content/tutorial.fsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
// it to define helpers that you do not want to show in the documentation.

#r @"../../src/FSharpPlus/bin/Release/netstandard2.0/FSharpPlus.dll"
#nowarn "0058" // We need to cheat a bit with indentation here.

(**
Introducing FSharpPlus
Expand All @@ -20,12 +19,11 @@ Introducing FSharpPlus
#r @"nuget: FSharpPlus"
```
*)
open FSharpPlus

(**
Ignore warnings about F# metadata if any.

(*** hide ***)
module [<AutoOpen>] E1 =

(**
Now we'll start with a quick overview of the features presented in F#+.

### Generic functions
Expand All @@ -36,33 +34,35 @@ here's an example with <code>map</code> ([fmap](https://wiki.haskell.org/Functor

*)

map string [|2;3;4;5|]
// val it : string [] = [|"2"; "3"; "4"; "5"|]
open FSharpPlus

map ((+) 9) (Some 3)
// val it : int option = Some 12
map string [|2;3;4;5|]
// val it : string [] = [|"2"; "3"; "4"; "5"|]

open FSharpPlus.Data
map ((+) 9) (Some 3)
// val it : int option = Some 12

map string (NonEmptyList.create 2 [3;4;5])
// val it : NonEmptyList<string> = {Head = "2"; Tail = ["3"; "4"; "5"];}
open FSharpPlus.Data

map string (NonEmptyList.create 2 [3;4;5])
// val it : NonEmptyList<string> = {Head = "2"; Tail = ["3"; "4"; "5"];}

(**
They're also available for your own types as long as they contain the appropriated method with the expected signature
*)


type Tree<'t> =
| Tree of 't * Tree<'t> * Tree<'t>
| Leaf of 't
static member Map (x:Tree<'a>, f) =
let rec loop f = function
| Leaf x -> Leaf (f x)
| Tree (x, t1, t2) -> Tree (f x, loop f t1, loop f t2)
loop f x

map ((*) 10) (Tree(6, Tree(2, Leaf 1, Leaf 3), Leaf 9))
// val it : Tree<int> = Tree (60,Tree (20,Leaf 10,Leaf 30),Leaf 90)
type Tree<'t> =
| Tree of 't * Tree<'t> * Tree<'t>
| Leaf of 't
static member Map (x:Tree<'a>, f) =
let rec loop f = function
| Leaf x -> Leaf (f x)
| Tree (x, t1, t2) -> Tree (f x, loop f t1, loop f t2)
loop f x
map ((*) 10) (Tree(6, Tree(2, Leaf 1, Leaf 3), Leaf 9))
// val it : Tree<int> = Tree (60,Tree (20,Leaf 10,Leaf 30),Leaf 90)

(**
Generic functions may be seen as an exotic thing in F# that only saves a few key strokes (<code>map</code> instead of <code>List.map</code> or <code>Array.map</code>) still they allow you to reach a higher abstraction level, using ad-hoc polymorphism.
Expand All @@ -72,67 +72,70 @@ But more interesting is the use of operators. You can't prefix them with the mod
Here you have a ready-to-use generic bind operator: ``>>=``
*)

let x = ["hello";" ";"world"] >>= (fun x -> Seq.toList x)
// val x : char list = ['h'; 'e'; 'l'; 'l'; 'o'; ' '; 'w'; 'o'; 'r'; 'l'; 'd']
let x = ["hello";" ";"world"] >>= (fun x -> Seq.toList x)
// val x : char list = ['h'; 'e'; 'l'; 'l'; 'o'; ' '; 'w'; 'o'; 'r'; 'l'; 'd']


let tryParseInt : string -> int option = tryParse
let tryDivide x n = if n = 0 then None else Some (x / n)
let tryParseInt : string -> int option = tryParse
let tryDivide x n = if n = 0 then None else Some (x / n)

let y = Some "20" >>= tryParseInt >>= tryDivide 100
// val y : int option = Some 5
let y = Some "20" >>= tryParseInt >>= tryDivide 100
// val y : int option = Some 5

(**
You have also the Kleisli composition (fish) operator: ``>=>``

Which is becoming popular in F# after the [Railway Oriented Programming](https://www.google.ch/#q=railway+oriented+programming) tutorial series
*)

let parseAndDivide100By = tryParseInt >=> tryDivide 100
let parseAndDivide100By = tryParseInt >=> tryDivide 100

let parsedAndDivide100By20 = parseAndDivide100By "20" // Some 5
let parsedAndDivide100By0' = parseAndDivide100By "zero" // None
let parsedAndDivide100By0 = parseAndDivide100By "0" // None
let parsedAndDivide100By20 = parseAndDivide100By "20" // Some 5
let parsedAndDivide100By0' = parseAndDivide100By "zero" // None
let parsedAndDivide100By0 = parseAndDivide100By "0" // None

let parseElement n = List.tryItem n >=> tryParseInt
let parsedElement = parseElement 2 ["0"; "1";"2"]
let parseElement n = List.tryItem n >=> tryParseInt
let parsedElement = parseElement 2 ["0"; "1";"2"]

(**
But don't forget the above used operators are generic, so we can change the type of our functions and we get a different functionality for free:
*)

(*** hide ***)
module E2 =
open FSharpPlus

(*** hide ***)
module E2 =

let tryParseInt x : Choice<int, string> =
match tryParse x with
| Some x -> Choice1Of2 x
| None -> Choice2Of2 ("Failed to parse " + x)
let tryParseInt x : Choice<int, string> =
match tryParse x with
| Some x -> Choice1Of2 x
| None -> Choice2Of2 ("Failed to parse " + x)


let tryDivide x n =
if n = 0 then Choice2Of2 "Can't divide by zero"
else Choice1Of2 (x / n)
let tryDivide x n =
if n = 0 then Choice2Of2 "Can't divide by zero"
else Choice1Of2 (x / n)

(**
The test code remains unchanged, but we get a more interesting functionality
*)

let parseAndDivide100By = tryParseInt >=> tryDivide 100
let parseAndDivide100By = tryParseInt >=> tryDivide 100

let parsedAndDivide100By20 = parseAndDivide100By "20" // Choice1Of2 5
let parsedAndDivide100By0' = parseAndDivide100By "zero" // Choice2Of2 "Failed to parse zero"
let parsedAndDivide100By0 = parseAndDivide100By "0" // Choice2Of2 "Can't divide by zero"
let parsedAndDivide100By20 = parseAndDivide100By "20" // Choice1Of2 5
let parsedAndDivide100By0' = parseAndDivide100By "zero" // Choice2Of2 "Failed to parse zero"
let parsedAndDivide100By0 = parseAndDivide100By "0" // Choice2Of2 "Can't divide by zero"


(**

Also when working with combinators, the generic applicative functor (space invaders) operator is very handy: ``<*>``
*)

let sumAllOptions = Some (+) <*> Some 2 <*> Some 10 // val sumAllOptions : int option = Some 12
let sumAllOptions = Some (+) <*> Some 2 <*> Some 10 // val sumAllOptions : int option = Some 12

let sumAllElemets = [(+)] <*> [10; 100] <*> [1; 2; 3] // int list = [11; 12; 13; 101; 102; 103]
let sumAllElemets = [(+)] <*> [10; 100] <*> [1; 2; 3] // int list = [11; 12; 13; 101; 102; 103]

(**

Expand All @@ -154,43 +157,43 @@ from https://github.com/ekmett/lens/wiki/Examples
First, open F#+ Lens
*)

open FSharpPlus.Lens
open FSharpPlus.Lens

(** Now, you can read from lenses (``_2`` is a lens for the second component of a tuple) *)

let r1 = ("hello","world")^._2
// val it : string = "world"
let r1 = ("hello","world")^._2
// val it : string = "world"

(** and you can write to lenses. *)
let r2 = setl _2 42 ("hello","world")
// val it : string * int = ("hello", 42)
let r2 = setl _2 42 ("hello","world")
// val it : string * int = ("hello", 42)

(** Composing lenses for reading (or writing) goes in the order an imperative programmer would expect, and just uses ``(<<)``. *)
let r3 = ("hello",("world","!!!"))^.(_2 << _1)
// val it : string = "world"
let r3 = ("hello",("world","!!!"))^.(_2 << _1)
// val it : string = "world"

let r4 = setl (_2 << _1) 42 ("hello",("world","!!!"))
// val it : string * (int * string) = ("hello", (42, "!!!"))
let r4 = setl (_2 << _1) 42 ("hello",("world","!!!"))
// val it : string * (int * string) = ("hello", (42, "!!!"))

(** You can make a Getter out of a pure function with ``to'``. *)
let r5 = "hello"^.to' length
// val it : int = 5
let r5 = "hello"^.to' length
// val it : int = 5

(** You can easily compose a Getter with a Lens just using ``(<<)``. No explicit coercion is necessary. *)
let r6 = ("hello",("world","!!!"))^. (_2 << _2 << to' length)
// val it : int = 3
let r6 = ("hello",("world","!!!"))^. (_2 << _2 << to' length)
// val it : int = 3

(** As we saw above, you can write to lenses and these writes can change the type of the container. ``(.->)`` is an infix alias for ``set``. *)
let r7 = _1 .-> "hello" <| ((),"world")
// val it : string * string = ("hello", "world")
let r7 = _1 .-> "hello" <| ((),"world")
// val it : string * string = ("hello", "world")

(** It can be used in conjunction with ``(|>)`` for familiar von Neumann style assignment syntax: *)
let r8 = ((), "world") |> _1 .-> "hello"
// val it : string * string = ("hello", "world")
let r8 = ((), "world") |> _1 .-> "hello"
// val it : string * string = ("hello", "world")

(** Conversely view, can be used as an prefix alias for ``(^.)``. *)
let r9 = view _2 (10,20)
// val it : int = 20
let r9 = view _2 (10,20)
// val it : int = 20

(**

Expand Down
Loading