FSharpPlus


Functors and Applicatives

#r @"nuget: FSharpPlus"
open FSharpPlus

You may run this script step-by-step The order of execution has to be respected since there are redefinitions of functions and operators

Functors

The intuitive definition is that a Functor is something you can map over.

So they all have a map operation which is their minimal definition.

Most containers are functors

let r01 = List.map   (fun x -> string (x + 10)) [ 1;2;3 ]
let r02 = Array.map  (fun x -> string (x + 10)) [|1;2;3|]
let r03 = Option.map (fun x -> string (x + 10)) (Some 5)

You can think of the Option functor as a particular case of a List that can be either empty or with just 1 element.

We could have used the generic function map from this library which works on any functor.

let r01' = map (fun x -> string (x + 10)) [ 1;2;3 ]
let r02' = map (fun x -> string (x + 10)) [|1;2;3|]
let r03' = map (fun x -> string (x + 10)) (Some 5)

Now let's define a simple type and make it a functor by adding a Map static method

type Id<'t> = Id of 't with
    static member Map (Id x, f) = Id (f x)

let r04 = map (fun x -> string (x + 10)) (Id 5)

Most computations are also functors

Here's an example with Async functions

let async5 = async.Return 5
let r05  = map (fun x -> string (x + 10)) async5
let r05' = Async.RunSynchronously r05

But even plain functions are functors

let r06  = map (fun x -> string (x + 10)) ((+) 2)
let r06' = r06 3

For functions map is equivalent to (<<) this means that mapping over a function is the same as composing the functions with the mapper

A List functor can be thought of as a function which takes an integer index to return a value: f: Naturals -> 't So, you can think of map on a List functor as composing a function:

*

let listFunc = function 0 -> 1 | 1 -> 2 | 2 -> 3 // [1;2;3]
let r01'' = map (fun x -> string (x + 10)) listFunc

What about tuples? *

module TupleFst = let map f (a,b) = (f a, b)
module TupleSnd = let map f (a,b) = (a, f b)

let r07 = TupleFst.map (fun x -> string (x + 10)) (5, "something else")
let r08 = TupleSnd.map (fun x -> string (x + 10)) ("something else", 5)

So there is more than one way to define a functor with tuples. The same applies to the Discriminated Union of 2 types.

// DUs
module ChoiceFst = let map f = function Choice1Of2 x -> Choice1Of2 (f x) | Choice2Of2 x -> Choice2Of2 x
module ChoiceSnd = let map f = function Choice2Of2 x -> Choice2Of2 (f x) | Choice1Of2 x -> Choice1Of2 x

let choiceValue1:Choice<int,string> = Choice1Of2 5
let choiceValue2:Choice<int,string> = Choice2Of2 "Can't divide by zero."

let r09  = ChoiceFst.map (fun x -> string (x + 10)) choiceValue1
let r09' = ChoiceFst.map (fun x -> string (x + 10)) choiceValue2

let r10  = ChoiceSnd.map (fun x -> "The error was: " + x) choiceValue1
let r10' = ChoiceSnd.map (fun x -> "The error was: " + x) choiceValue2

Tree as a functor

type Tree<'a> =
    | Tree of 'a * Tree<'a> * Tree<'a>
    | Leaf of 'a

module Tree = let rec map f = function 
                | Leaf x        -> Leaf (f x) 
                | Tree(x,t1,t2) -> Tree(f x, map f t1, map f t2)

let myTree = Tree(6, Tree(2, Leaf 1, Leaf 3), Leaf 9)

let r11 = Tree.map (fun x -> string (x + 10)) myTree

Q: is String a Functor?

let r12 = String.map (fun c -> System.Char.ToUpper(c)) "Hello world"

A: Kind of, but we can't change the wrapped type. We're stick to ('a->'a) -> C<'a> -> C<'a> if we assume 'a = char and C<'a> = String

Finally there are some laws:

Limitations:

We can define map2 then map3 then .. mapN ?

type Option<'T> with
    static member map2 f x y = 
        match x, y with
        | Some x, Some y -> Some (f x y)
        | _              -> None

    static member map3 f x y z = 
        match x, y, z with
        | Some x, Some y, Some z -> Some (f x y z)
        | _                      -> None

let r13 = Option.map2 (+) (Some 2) (Some 3)

let r14 = List.map2 (+) [1;2;3] [10;11;12]

let add3 a b c = a + b + c

let r15 = Option.map3 add3 (Some 2) (Some 2) (Some 1)

Question: Is it possible to generalize to mapN?

Applicative Functors

What if we split map in 2 steps?

// map ('a -> 'b) -> C<'a> -> C<'b>
//     \--------/    \---/    \---/
//         (a)        (b)      (c)
//
// 1)    ('a -> 'b)        ->  C<'a -> 'b>
//       \--------/            \---------/
//           (a)                      
//               
// 2)  C<'a -> 'b> -> C<'a>  ->   C<'b>
//     \---------/    \---/       \---/
//                     (b)         (c)
//
//
// step1   ('a -> 'b)        ->  "C<'a -> 'b>"      Put the function into a context C
// step2 "C<'a -> 'b>" C<'a> ->   C<'b>             Apply the function in a context C to a value in a context C

Here's an example with Options

let step1 f = Some f
let step2 a b = 
    match a, b with
    | Some f, Some x -> Some (f x)
    | _              -> None

let r16 = step1 (fun x -> string (x + 10))
let r17 = step2 r16 (Some 5)

    

So now instead of writing:

let r18  = Option.map (fun x -> string (x + 10)) (Some 5)

we write

let r18' = step2 (step1 (fun x -> string (x + 10))) (Some 5)

    

and instead of map2 like this:

let r19   = Option.map2 (+) (Some 2) (Some 3)

we write

let r19i  = step2 (step1 (+)) (Some 2)

.. and finally

let r19' = step2 r19i (Some 3)

by applying step2 again. We can apply step2 again if the result is still a function in a container, just like partial application.

lets give names to step1 and step2: pure and <*>

module OptionAsApplicative =
    let pure' x = Some x
    let (<*>) a b = 
        match a, b with
        | Some f, Some x -> Some (f x)
        | _              -> None

open OptionAsApplicative

let r18''  = Option.map (fun x -> string (x + 10)) (Some 5)

let r18''' = Some (fun x -> string (x + 10)) <*> Some 5
// analog to:
let r18'''' =     (fun x -> string (x + 10))          5

Now with map3 (and further with mapN)

let r20 = Option.map3 add3 (Some 2) (Some 2) (Some 1)

let r20'  = Some add3 <*> Some 2 <*> Some 2 <*> Some 1
// analog to:
let r20''  =     add3          2          2          1

but even without add3 we can write 1 + 2 + 2 which is 1 + (2 + 2) and the same as:

let r20'''  = (+) 1 ((+) 2 2)

with options becomes:

let r20'''' = Some (+) <*> Some 1 <*> (Some (+) <*> Some 2 <*> Some 2)

constrast it with

let r20'''''  =    (+)          1     (     (+)          2          2)

we know apply is (<|) in F#

let r21     =      (+) <|       1 <|  (     (+) <|       2 <|       2)
let r21'    = Some (+) <*> Some 1 <*> (Some (+) <*> Some 2 <*> Some 2)

So at this point the name "Applicative Functor" should make sense

Q: Isn't it easier to do just Some ( (+) 1 ((+) 2 2) ) ? We get the same result in the end. A: Yes, in this particular case it's the same but what if instead of Some 1 we have None

let r22   = Some (+) <*> None <*> (Some (+) <*> Some 2 <*> Some 2)

That's because we're applying functions inside a context.

It looks the same as applying outside but in fact some effects occurs behind the scenes.

To have a better idea let's move out of Option:

[<AutoOpen>]
module Async =
    let pure' x = async.Return x
    let (<*>) f x = async.Bind(f, fun x1 -> async.Bind(x, fun x2 -> pure'(x1 x2)))

    
let r23   = async {return (+)} <*> async {return 2} <*> async {return 3}

let r23'  = pure' (+) <*> pure' 2 <*> pure' 3

try Async.RunSynchronously r23'

let getLine = async { 
        let x = System.Console.ReadLine() 
        return  System.Int32.Parse x
    }

let r24  = pure' (+) <*> getLine <*> getLine

try Async.RunSynchronously r24

module ListAsApplicative =
    let pure' x = [x]        
    let (<*>)  f x = List.collect (fun x1 -> List.collect (fun x2 -> [x1 x2]) x) f

    (* here are two other possible implementations of (<*>) for List
    let (<*>) f x = f |> List.map (fun f -> x |> List.map (fun x -> f x)) |> List.concat
    let (<*>) f x= 
        seq {
                for f in f do
                for x in x do
                yield f x} |> Seq.toList *)

open ListAsApplicative

let r25 =  List.map (fun x -> string (x + 10)) [1;2;3]

let r25'  =       [fun x -> string (x + 10)] <*> [1..3]
let r25'' = pure' (fun x -> string (x + 10)) <*> [1..3]


let r26 = [string; fun x -> string (x + 10)] <*> [1;2;3]

So, for lists map2 is equivalent to write:

let r27 = [(+)] <*> [1;2] <*> [10;20;30]

let r28 = [(+);(-)] <*> [1;2] <*> [10;20;30]


    
module SeqAsApplicative =
    let pure' x = Seq.initInfinite (fun _ -> x)
    let (<*>) f x = Seq.zip f x |> Seq.map (fun (f,x) -> f x)

open SeqAsApplicative


let r29 =  Seq.map (fun x -> string (x + 10))    (seq [1;2;3])          |> Seq.toList
let r29' =   pure' (fun x -> string (x + 10)) <*> seq [1;2;3]           |> Seq.toList
    
let r30 = seq [(+);(-)] <*> seq [1;2] <*> seq [10;20;30]                |> Seq.toList  // compare it with r28

An exotic case where there is no pure.

module MapAsApplicative = 
    let (<*>) (f:Map<'k,_>) x =
        Map (seq {
            for KeyValue(k, vf) in f do
                match Map.tryFind k x with
                | Some vx -> yield k, vf vx
                | _       -> () })


open MapAsApplicative

let r31 = Map ['a',(+);'b',(-)] <*> Map ['a',1;'b',2] <*> Map ['a',10;'b',20;'c',30] 

let r32 = Map ['c',(+);'b',(-)] <*> Map ['a',1;'b',2] <*> Map ['a',10;'b',20;'c',30] 

Monads

open OptionAsApplicative    
    
let a = Some 3
let b = Some 2
let c = Some 1

let half x = x / 2        

let f a b c =
    let x = a + b
    let y = half c
    x + y

let f' a b c =
    let x = Some (+)  <*> a <*> b
    let y = Some half <*> c
    Some (+) <*> x <*> y
    
let r33 = f' (Some 1) (Some 2) (Some 3)

let r33' = f' None (Some 2) (Some 3)
    

OK, but if I want to use a function like:

let exactHalf x =
    if x % 2 = 0 then Some (x / 2)
    else None

It doesn't fit

// let f'' a b c =
//     let x = Some (+) <*> a <*> b
//     let y = Some exactHalf <*> c   // y will be inferred as option<option<int>>
//     Some (+) <*> x <*> y           // so this will not compile

The problem is, we were working with ordinary functions. When we lift these function into C, we get functions wrapped in contexts. With Applicatives we can use either a function in a context which is ready to use or an ordinary function, which we can lift easily with pure.

But exactHalf is a different thing: its signature is int -> Option<int>. This function goes from a pure value to a value in a context, so either:

1) we use it directly but we first need to extract the argument from the context.

2) we use it in an Applicative, we will get a value in a context in another context, so we will need to flatten both contexts.

Monad provides solutions to both alternatives

// bind : C<'a> -> ('a->C<'b>) -> C<'b>
// join : C<C<'a>> -> C<'a>

module OptionAsMonad =
    let join  x   = Option.bind id x
    let (>>=) x f = Option.bind f x
    // in monads pure' is called return, unit or result, but it's essentially the same function.
    let return' x = Some x
        
open OptionAsMonad        



let f'' a b c =
    let x = Some (+) <*> a <*> b
    let y = Some exactHalf <*> c |> join
    Some (+) <*> x <*> y
    

let f''' a b c =
    let x = Some (+) <*> a <*> b
    let y = c >>= exactHalf
    Some (+) <*> x <*> y

All monads are automatically applicatives, remember <*> for lists, it was:

let (<*>) f x = List.collect (fun x1 -> List.collect (fun x2 -> [x1 x2]) x) f

But List.collect is in fact bind, and [x1 x2] is pure (x1 x2)

// let (<*>) f x = f >>= (fun x1 -> x >>= (fun x2 -> pure' (x1 x2)))

And this definition of <*> applies to all monads.

Q: but we said all applicatives are functors, so monads should be functors as well, right? A: Yes, they are, and this is the general definition of map based on bind and result (aka return or pure)

let map f x = x >>= (pure' << f)

Recommended links

Same explanation but with pictures http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html

Haskell typeclasses http://www.haskell.org/haskellwiki/Typeclassopedia

namespace FSharpPlus
val r01: string list
Multiple items
module List from FSharpPlus
<summary> Additional operations on List </summary>

--------------------
module List from Microsoft.FSharp.Collections

--------------------
type List<'T> = | op_Nil | op_ColonColon of Head: 'T * Tail: 'T list interface IReadOnlyList<'T> interface IReadOnlyCollection<'T> interface IEnumerable interface IEnumerable<'T> member GetReverseIndex: rank: int * offset: int -> int member GetSlice: startIndex: int option * endIndex: int option -> 'T list static member Cons: head: 'T * tail: 'T list -> 'T list member Head: 'T member IsEmpty: bool member Item: index: int -> 'T with get ...
val map: mapping: ('T -> 'U) -> list: 'T list -> 'U list
val x: int
Multiple items
val string: value: 'T -> string

--------------------
type string = System.String
val r02: string array
Multiple items
module Array from FSharpPlus
<summary> Additional operations on Array </summary>

--------------------
module Array from Microsoft.FSharp.Collections
val map: mapping: ('T -> 'U) -> array: 'T array -> 'U array
val r03: string option
Multiple items
module Option from FSharpPlus
<summary> Additional operations on Option </summary>

--------------------
module Option from Microsoft.FSharp.Core
val map: mapping: ('T -> 'U) -> option: 'T option -> 'U option
union case Option.Some: Value: 'T -> Option<'T>
val r01': string list
val map: f: ('T -> 'U) -> x: 'Functor<'T> -> 'Functor<'U> (requires member Map)
<summary>Lifts a function into a Functor.</summary>
<category index="1">Functor</category>
val r02': string array
val r03': string option
't
Multiple items
union case Id.Id: 't -> Id<'t>

--------------------
type Id<'t> = | Id of 't static member Map: Id<'a> * f: ('a -> 'b) -> Id<'b>
Multiple items
module Map from FSharpPlus
<summary> Additional operations on Map&lt;'Key, 'Value&gt; </summary>

--------------------
module Map from Microsoft.FSharp.Collections

--------------------
type Map<'Key,'Value (requires comparison)> = interface IReadOnlyDictionary<'Key,'Value> interface IReadOnlyCollection<KeyValuePair<'Key,'Value>> interface IEnumerable interface IComparable interface IEnumerable<KeyValuePair<'Key,'Value>> interface ICollection<KeyValuePair<'Key,'Value>> interface IDictionary<'Key,'Value> new: elements: seq<'Key * 'Value> -> Map<'Key,'Value> member Add: key: 'Key * value: 'Value -> Map<'Key,'Value> member Change: key: 'Key * f: ('Value option -> 'Value option) -> Map<'Key,'Value> ...

--------------------
new: elements: seq<'Key * 'Value> -> Map<'Key,'Value>
val x: 'a
val f: ('a -> 'b)
val r04: Id<string>
val async5: Async<int>
val async: AsyncBuilder
member AsyncBuilder.Return: value: 'T -> Async<'T>
val r05: Async<string>
val r05': string
Multiple items
module Async from FSharpPlus
<summary> Additional operations on Async </summary>

--------------------
type Async = static member AsBeginEnd: computation: ('Arg -> Async<'T>) -> ('Arg * AsyncCallback * obj -> IAsyncResult) * (IAsyncResult -> 'T) * (IAsyncResult -> unit) static member AwaitEvent: event: IEvent<'Del,'T> * ?cancelAction: (unit -> unit) -> Async<'T> (requires delegate and 'Del :> Delegate) static member AwaitIAsyncResult: iar: IAsyncResult * ?millisecondsTimeout: int -> Async<bool> static member AwaitTask: task: Task<'T> -> Async<'T> + 1 overload static member AwaitWaitHandle: waitHandle: WaitHandle * ?millisecondsTimeout: int -> Async<bool> static member CancelDefaultToken: unit -> unit static member Catch: computation: Async<'T> -> Async<Choice<'T,exn>> static member Choice: computations: seq<Async<'T option>> -> Async<'T option> static member FromBeginEnd: beginAction: (AsyncCallback * obj -> IAsyncResult) * endAction: (IAsyncResult -> 'T) * ?cancelAction: (unit -> unit) -> Async<'T> + 3 overloads static member FromContinuations: callback: (('T -> unit) * (exn -> unit) * (OperationCanceledException -> unit) -> unit) -> Async<'T> ...

--------------------
type Async<'T>
static member Async.RunSynchronously: computation: Async<'T> * ?timeout: int * ?cancellationToken: System.Threading.CancellationToken -> 'T
val r06: (int -> string)
val r06': string
val listFunc: _arg1: int -> int
val r01'': (int -> string)
val map: f: ('a -> 'b) -> a: 'a * b: 'c -> 'b * 'c
val a: 'a
val b: 'c
val map: f: ('a -> 'b) -> a: 'c * b: 'a -> 'c * 'b
val a: 'c
val b: 'a
val r07: string * string
module TupleFst from Applicative-functors
val r08: string * string
module TupleSnd from Applicative-functors
val map: f: ('a -> 'b) -> _arg1: Choice<'a,'c> -> Choice<'b,'c>
union case Choice.Choice1Of2: 'T1 -> Choice<'T1,'T2>
union case Choice.Choice2Of2: 'T2 -> Choice<'T1,'T2>
val x: 'c
val map: f: ('a -> 'b) -> _arg1: Choice<'c,'a> -> Choice<'c,'b>
val choiceValue1: Choice<int,string>
Multiple items
module Choice from FSharpPlus
<summary> Additional operations on Choice </summary>

--------------------
type Choice<'T1,'T2> = | Choice1Of2 of 'T1 | Choice2Of2 of 'T2

--------------------
type Choice<'T1,'T2,'T3> = | Choice1Of3 of 'T1 | Choice2Of3 of 'T2 | Choice3Of3 of 'T3

--------------------
type Choice<'T1,'T2,'T3,'T4> = | Choice1Of4 of 'T1 | Choice2Of4 of 'T2 | Choice3Of4 of 'T3 | Choice4Of4 of 'T4

--------------------
type Choice<'T1,'T2,'T3,'T4,'T5> = | Choice1Of5 of 'T1 | Choice2Of5 of 'T2 | Choice3Of5 of 'T3 | Choice4Of5 of 'T4 | Choice5Of5 of 'T5

--------------------
type Choice<'T1,'T2,'T3,'T4,'T5,'T6> = | Choice1Of6 of 'T1 | Choice2Of6 of 'T2 | Choice3Of6 of 'T3 | Choice4Of6 of 'T4 | Choice5Of6 of 'T5 | Choice6Of6 of 'T6

--------------------
type Choice<'T1,'T2,'T3,'T4,'T5,'T6,'T7> = | Choice1Of7 of 'T1 | Choice2Of7 of 'T2 | Choice3Of7 of 'T3 | Choice4Of7 of 'T4 | Choice5Of7 of 'T5 | Choice6Of7 of 'T6 | Choice7Of7 of 'T7
Multiple items
val int: value: 'T -> int (requires member op_Explicit)

--------------------
type int = int32

--------------------
type int<'Measure> = int
val choiceValue2: Choice<int,string>
val r09: Choice<string,string>
module ChoiceFst from Applicative-functors
val r09': Choice<string,string>
val r10: Choice<int,string>
module ChoiceSnd from Applicative-functors
val x: string
val r10': Choice<int,string>
Multiple items
union case Tree.Tree: 'a * Tree<'a> * Tree<'a> -> Tree<'a>

--------------------
type Tree<'a> = | Tree of 'a * Tree<'a> * Tree<'a> | Leaf of 'a
'a
union case Tree.Leaf: 'a -> Tree<'a>
type Tree<'a> = | Tree of 'a * Tree<'a> * Tree<'a> | Leaf of 'a
val map: f: ('a -> 'b) -> _arg1: Tree<'a> -> Tree<'b>
val t1: Tree<'a>
val t2: Tree<'a>
val myTree: Tree<int>
Multiple items
union case Tree.Tree: 'a * Tree<'a> * Tree<'a> -> Tree<'a>

--------------------
module Tree from Applicative-functors

--------------------
type Tree<'a> = | Tree of 'a * Tree<'a> * Tree<'a> | Leaf of 'a
val r11: Tree<string>
val r12: string
Multiple items
module String from FSharpPlus
<summary> Additional operations on String </summary>

--------------------
module String from Microsoft.FSharp.Core
val map: mapping: (char -> char) -> str: string -> string
val c: char
namespace System
[<Struct>] type Char = member CompareTo: value: char -> int + 1 overload member Equals: obj: char -> bool + 1 overload member GetHashCode: unit -> int member GetTypeCode: unit -> TypeCode member ToString: unit -> string + 2 overloads static member ConvertFromUtf32: utf32: int -> string static member ConvertToUtf32: highSurrogate: char * lowSurrogate: char -> int + 1 overload static member GetNumericValue: c: char -> float + 1 overload static member GetUnicodeCategory: c: char -> UnicodeCategory + 1 overload static member IsAscii: c: char -> bool ...
<summary>Represents a character as a UTF-16 code unit.</summary>
System.Char.ToUpper(c: char) : char
System.Char.ToUpper(c: char, culture: System.Globalization.CultureInfo) : char
'T
val f: ('a -> 'b -> 'c)
val x: 'a option
val y: 'b option
val y: 'b
union case Option.None: Option<'T>
val f: ('a -> 'b -> 'c -> 'd)
val z: 'c option
val z: 'c
val r13: int option
Multiple items
val map2: mapping: ('T1 -> 'T2 -> 'U) -> option1: 'T1 option -> option2: 'T2 option -> 'U option

--------------------
static member Option.map2: f: ('a -> 'b -> 'c) -> x: 'a option -> y: 'b option -> 'c option
val r14: int list
val map2: mapping: ('T1 -> 'T2 -> 'U) -> list1: 'T1 list -> list2: 'T2 list -> 'U list
val add3: a: int -> b: int -> c: int -> int
val a: int
val b: int
val c: int
val r15: int option
Multiple items
val map3: mapping: ('T1 -> 'T2 -> 'T3 -> 'U) -> option1: 'T1 option -> option2: 'T2 option -> option3: 'T3 option -> 'U option

--------------------
static member Option.map3: f: ('a -> 'b -> 'c -> 'd) -> x: 'a option -> y: 'b option -> z: 'c option -> 'd option
val step1: f: 'a -> 'a option
val f: 'a
val step2: a: ('a -> 'b) option -> b: 'a option -> 'b option
val a: ('a -> 'b) option
val b: 'a option
val r16: (int -> string) option
val r17: string option
val r18: string option
val r18': string option
val r19: int option
val r19i: (int -> int) option
val r19': int option
val pure': x: 'a -> 'a option
module OptionAsApplicative from Applicative-functors
val r18'': string option
val r18''': string option
val r18'''': string
val r20: int option
val r20': int option
val r20'': int
val r20''': int
val r20'''': int option
val r20''''': int
val r21: int
val r21': int option
val r22: int option
Multiple items
type AutoOpenAttribute = inherit Attribute new: unit -> AutoOpenAttribute + 1 overload member Path: string

--------------------
new: unit -> AutoOpenAttribute
new: path: string -> AutoOpenAttribute
val pure': x: 'a -> Async<'a>
val f: Async<('a -> 'b)>
val x: Async<'a>
member AsyncBuilder.Bind: computation: Async<'T> * binder: ('T -> Async<'U>) -> Async<'U>
val x1: ('a -> 'b)
val x2: 'a
val r23: Async<int>
val r23': Async<int>
val getLine: Async<int>
type Console = static member Beep: unit -> unit + 1 overload static member Clear: unit -> unit static member GetCursorPosition: unit -> struct (int * int) static member MoveBufferArea: sourceLeft: int * sourceTop: int * sourceWidth: int * sourceHeight: int * targetLeft: int * targetTop: int -> unit + 1 overload static member OpenStandardError: unit -> Stream + 1 overload static member OpenStandardInput: unit -> Stream + 1 overload static member OpenStandardOutput: unit -> Stream + 1 overload static member Read: unit -> int static member ReadKey: unit -> ConsoleKeyInfo + 1 overload static member ReadLine: unit -> string ...
<summary>Represents the standard input, output, and error streams for console applications. This class cannot be inherited.</summary>
System.Console.ReadLine() : string
[<Struct>] type Int32 = member CompareTo: value: int -> int + 1 overload member Equals: obj: int -> bool + 1 overload member GetHashCode: unit -> int member GetTypeCode: unit -> TypeCode member ToString: unit -> string + 3 overloads member TryFormat: destination: Span<char> * charsWritten: byref<int> * ?format: ReadOnlySpan<char> * ?provider: IFormatProvider -> bool static member Abs: value: int -> int static member Clamp: value: int * min: int * max: int -> int static member CopySign: value: int * sign: int -> int static member CreateChecked<'TOther (requires 'TOther :> INumberBase<'TOther>)> : value: 'TOther -> int ...
<summary>Represents a 32-bit signed integer.</summary>
System.Int32.Parse(s: string) : int
System.Int32.Parse(s: string, provider: System.IFormatProvider) : int
System.Int32.Parse(s: string, style: System.Globalization.NumberStyles) : int
System.Int32.Parse(s: System.ReadOnlySpan<char>, provider: System.IFormatProvider) : int
System.Int32.Parse(s: string, style: System.Globalization.NumberStyles, provider: System.IFormatProvider) : int
System.Int32.Parse(s: System.ReadOnlySpan<char>, ?style: System.Globalization.NumberStyles, ?provider: System.IFormatProvider) : int
val r24: Async<int>
val pure': x: 'a -> 'a list
val f: ('a -> 'b) list
val x: 'a list
val collect: mapping: ('T -> 'U list) -> list: 'T list -> 'U list
module ListAsApplicative from Applicative-functors
val r25: string list
val r25': string list
val r25'': string list
val r26: string list
val r27: int list
val r28: int list
val pure': x: 'a -> seq<'a>
Multiple items
module Seq from FSharpPlus.Operators

--------------------
module Seq from FSharpPlus
<summary> Additional operations on Seq </summary>

--------------------
module Seq from Microsoft.FSharp.Collections
val initInfinite: initializer: (int -> 'T) -> seq<'T>
val f: seq<('a -> 'b)>
val x: seq<'a>
val zip: source1: seq<'T1> -> source2: seq<'T2> -> seq<'T1 * 'T2>
val map: mapping: ('T -> 'U) -> source: seq<'T> -> seq<'U>
module SeqAsApplicative from Applicative-functors
val r29: string list
Multiple items
val seq: sequence: seq<'T> -> seq<'T>

--------------------
type seq<'T> = System.Collections.Generic.IEnumerable<'T>
val toList: source: seq<'T> -> 'T list
val r29': string list
val r30: int list
val f: Map<'k,('a -> 'b)> (requires comparison)
'k
val x: Map<'k,'a> (requires comparison)
active recognizer KeyValue: System.Collections.Generic.KeyValuePair<'Key,'Value> -> 'Key * 'Value
val k: 'k (requires comparison)
val vf: ('a -> 'b)
val tryFind: key: 'Key -> table: Map<'Key,'T> -> 'T option (requires comparison)
val vx: 'a
module MapAsApplicative from Applicative-functors
val r31: Map<char,int>
val r32: Map<char,int>
val a: int option
val b: int option
val c: int option
val half: x: int -> int
val f: a: int -> b: int -> c: int -> int
val y: int
val f': a: int option -> b: int option -> c: int option -> int option
val x: int option
val y: int option
val r33: int option
val r33': int option
val exactHalf: x: int -> int option
val join: x: 'a option option -> 'a option
val x: 'a option option
val bind: binder: ('T -> 'U option) -> option: 'T option -> 'U option
val id: x: 'T -> 'T
val f: ('a -> 'b option)
val return': x: 'a -> 'a option
module OptionAsMonad from Applicative-functors
val f'': a: int option -> b: int option -> c: int option -> int option
val f''': a: int option -> b: int option -> c: int option -> int option
val map: f: ('a -> 'b) -> x: 'a option -> 'b option