A functor with application, providing operations to embed pure expressions (return
), and sequence computations and combine their results (<*>
).
___
return x
/ result x
(<*>) f x
static member Return (x: 'T) : 'Applicative<'T>
static member (<*>) (f: 'Applicative<'T -> 'U>, x: 'Applicative<'T>) : 'Applicative<'U>
Note: return
can't be used outside computation expressions, use result
instead.
static member Lift2 (f: 'T1 -> 'T2 -> 'T, x1: 'Applicative<'T1>, x2: 'Applicative<'T2>) : 'Applicative<'T>
result id <*> v = v
result (<<) <*> u <*> v <*> w = u <*> (v <*> w)
result f <*> result x = result (f x)
u <*> result y = result ((|>) y) <*> u
Functor: An applicative is a functor whose map
operation can be splitted in return
and (<*>)
operations,
-
Monad : Monads are functors with an additional Join
operation,
From F#
seq<'T>
list<'T>
array<'T>
'T [,]
'T [,,]
'T [,,,]
option<'T>
voption<'T>
IObservable<'T>
Lazy<'T>
Async<'T>
Result<'T, 'U>
Choice<'T, 'U>
KeyValuePair<'Key, 'T>
'Monoid * 'T
ValueTuple<'Monoid, 'T>
Task<'T>
ValueTask<'T>
'R -> 'T
Expr<'T>
ResizeArray<'T>
From F#+
Identity<'T>
Cont<'R, 'T>
ContT<'R, 'T>
Reader<'R, 'T>
ReaderT<'R, 'Monad<'T>>
Writer<'Monoid, 'T>
WriterT<'Monad<'T * 'Monoid>>
State<'S, 'T * 'S>
StateT<'S, 'Monad<'T * 'S>>
OptionT<'Monad<option<'T>>
ValueOptionT<'Monad<voption<'T>>
SeqT<'Monad<seq<'T>>
ListT<'Monad<list<'T>>
ResultT<'Monad<Result<'T, 'TError>>
ChoiceT<'Monad<Choice<'T, 'TError>>
Free<'Functor<'T>, 'T>
NonEmptyList<'T>
Validation<'Error, 'T>
ZipList<'T>
ParallelArray<'T>
Const<'C, 'T>
Compose<'Applicative1<'Applicative2<'T>>>
DList<'T>
Vector<'T, 'Dimension>
Matrix<'T, 'Rows, 'Columns>
Restricted:
- string
- StringBuilder
- Set<'T>
- IEnumerator<'T>
Only for <*> operation:
- Map<'Key, 'T>
- Dictionary<'Key, 'T>
- IDictionary<'Key, 'T>
- IReadOnlyDictionary<'Key, 'T>
Suggest another concrete implementation
open FSharpPlus
open FSharpPlus.Data
// Apply +4 to a list
let lst5n6 = map ((+) 4) [ 1; 2 ]
// Apply +4 to an array
let arr5n6 = map ((+) 4) [|1; 2|]
// I could have written this
let arr5n6' = (+) <!> [|4|] <*> [|1; 2|]
// Add two options
let opt120 = (+) <!> Some 20 <*> tryParse "100"
// Applicatives need Return (result)
// Test return
let resSome22 : option<_> = result 22
let resSing22 : list<_> = result 22
let resLazy22 : Lazy<_> = result 22
let (quot5 : Microsoft.FSharp.Quotations.Expr<int>) = result 5
// Example
type Person = { Name: string; Age: int } with static member create n a = { Name = n; Age = a }
let person1 = Person.create <!> tryHead ["gus"] <*> tryParse "42"
let person2 = Person.create <!> tryHead ["gus"] <*> tryParse "fourty two"
let person3 = Person.create <!> tryHead ["gus"] <*> (tryHead ["42"] >>= tryParse)
// Other ways to write applicative expressions
// Function lift2 helps in many cases
let person1' = (tryHead ["gus"], tryParse "42") ||> lift2 Person.create
let person2' = (tryHead ["gus"], tryParse "fourty two") ||> lift2 Person.create
let person3' = (tryHead ["gus"], tryHead ["42"] >>= tryParse) ||> lift2 Person.create
// Using Idiom brackets from http://www.haskell.org/haskellwiki/Idiom_brackets
let res3n4 = iI ((+) 2) [1;2] Ii
let res3n4' = iI (+) (result 2) [1;2] Ii
let res18n24 = iI (+) (ZipList(seq [8;4])) (ZipList(seq [10;20])) Ii
let tryDiv x y = if y = 0 then None else Some (x </div/> y)
let resSome3 = join (iI tryDiv (Some 6) (Some 2) Ii)
let resSome3' = iI tryDiv (Some 6) (Some 2) Ji
let tryDivBy y = if y = 0 then None else Some (fun x -> x </div/> y)
let resSome2 = join (result tryDivBy <*> Some 4) <*> Some 8
let resSome2' = join ( iI tryDivBy (Some 4) Ii) <*> Some 8
let resSome2'' = iI tryDivBy (Some 4) J (Some 8) Ii
let resNone = iI tryDivBy (Some 0) J (Some 8) Ii
let res16n17 = iI (+) (iI (+) (result 4) [2; 3] Ii) [10] Ii
let opt121 = iI (+) (Some 21) (tryParse "100") Ii
let opt122 = iI tryDiv (tryParse "488") (trySqrt 16) Ji
// Using applicative math operators
open FSharpPlus.Math.Applicative
let opt121' = Some 21 .+. tryParse "100"
let optTrue = 30 >. tryParse "29"
let optFalse = tryParse "30" .< 29
let m1m2m3 = -.[1; 2; 3]
// Using applicative computation expression
let getName s = tryHead s
let getAge s = tryParse s
let person4 = applicative {
let! name = getName ["gus"]
and! age = getAge "42"
return { Name = name; Age = age } }
Unlike monads, applicatives are always composable.
The date type Compose<'Applicative1<'Applicative2<'T>>>
can be used to compose any 2 applicatives:
let res4 = (+) <!> Compose [Some 3] <*> Compose [Some 1]
let getNameAsync s = async { return tryHead s }
let getAgeAsync s = async { return tryParse s }
let person5 = Person.create <!> Compose (getNameAsync ["gus"]) <*> Compose (getAgeAsync "42")
The computation expressions applicative2 and applicative3 can also be used to compose applicatives:
let person6 = applicative2 {
let! name = printfn "aa"; getNameAsync ["gus"]
and! age = getAgeAsync "42"
return { Name = name; Age = age } }
// A Monad is automatically an Applicative
type MyList<'s> = MyList of 's seq with
static member Return (x: 'a) = MyList (Seq.singleton x)
static member (>>=) (MyList x: MyList<'T>, f) = MyList (Seq.collect (f >> (fun (MyList x) -> x)) x)
let mappedMyList : MyList<_> = (MyList [(+) 1; (+) 2; (+) 3]) <*> (MyList [1; 2; 3])
-
Highly recommended Matt Thornton's blog Grokking Applicatives.
It contains examples using F#+ and an explanation from scratch.
val id: x: 'T -> 'T
namespace FSharpPlus
namespace FSharpPlus.Data
val lst5n6: int 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 arr5n6: int array
val arr5n6': int array
val opt120: int option
union case Option.Some: Value: 'T -> Option<'T>
val tryParse: value: string -> 'T option (requires member TryParse)
<summary>
Converts to a value from its string representation. Returns None if the convertion doesn't succeed.
</summary>
<category index="21">Converter</category>
val resSome22: int option
Multiple items
val option: f: ('g -> 'h) -> n: 'h -> _arg1: 'g option -> 'h
<summary>
Takes a function, a default value and a option value. If the option value is None, the function returns the default value.
Otherwise, it applies the function to the value inside Some and returns the result.
</summary>
<category index="0">Common Combinators</category>
--------------------
type 'T option = Option<'T>
val result: x: 'T -> 'Functor<'T> (requires member Return)
<summary>
Lifts a value into a Functor. Same as return in Computation Expressions.
</summary>
<category index="2">Applicative</category>
val resSing22: int list
type 'T list = List<'T>
val resLazy22: Lazy<int>
Multiple items
active recognizer Lazy: Lazy<'T> -> 'T
--------------------
module Lazy
from FSharpPlus
<summary>
Additional operations on Lazy - delayed computations
</summary>
--------------------
type Lazy<'T> = System.Lazy<'T>
val quot5: Quotations.Expr<int>
namespace Microsoft
namespace Microsoft.FSharp
namespace Microsoft.FSharp.Quotations
Multiple items
type Expr =
override Equals: obj: obj -> bool
member GetFreeVars: unit -> Var seq
member Substitute: substitution: (Var -> Expr option) -> Expr
member ToString: full: bool -> string
static member AddressOf: target: Expr -> Expr
static member AddressSet: target: Expr * value: Expr -> Expr
static member Application: functionExpr: Expr * argument: Expr -> Expr
static member Applications: functionExpr: Expr * arguments: Expr list list -> Expr
static member Call: methodInfo: MethodInfo * arguments: Expr list -> Expr + 1 overload
static member CallWithWitnesses: methodInfo: MethodInfo * methodInfoWithWitnesses: MethodInfo * witnesses: Expr list * arguments: Expr list -> Expr + 1 overload
...
--------------------
type Expr<'T> =
inherit Expr
member Raw: Expr
Multiple items
val int: value: 'T -> int (requires member op_Explicit)
--------------------
type int = int32
--------------------
type int<'Measure> =
int
Person.Name: string
Multiple items
val string: value: 'T -> string
--------------------
type string = System.String
Person.Age: int
val n: string
val a: int
val person1: Person option
type Person =
{
Name: string
Age: int
}
static member create: n: string -> a: int -> Person
static member Person.create: n: string -> a: int -> Person
val tryHead: source: 'Foldable<'T> -> 'T option (requires member TryHead)
<summary>Gets the first element of the foldable, or
<c>None</c> if the foldable is empty.</summary>
<category index="11">Foldable</category>
<param name="source">The input foldable.</param>
<returns>The first element of the foldable or None.</returns>
val person2: Person option
val person3: Person option
val person1': Person option
val lift2: f: ('T -> 'U -> 'V) -> x: 'Applicative<'T> -> y: 'Applicative<'U> -> 'Applicative<'V> (requires member Lift2)
<summary>
Applies 2 lifted arguments to a non-lifted function. Equivalent to map2 in non list-like types.
</summary>
<category index="2">Applicative</category>
val person2': Person option
val person3': Person option
val res3n4: int list
val iI: x: 'a -> ('b -> 'd) (requires member ($) and member Return)
Multiple items
union case Ii.Ii: Ii
--------------------
type Ii = | Ii
val res3n4': int list
val res18n24: ZipList<int>
Multiple items
union case ZipList.ZipList: 's seq -> ZipList<'s>
--------------------
module ZipList
from FSharpPlus.Data
<summary>
Basic operations on ZipList
</summary>
--------------------
type ZipList<'s> =
| ZipList of 's seq
member Item: n: int -> 's
static member (+) : x: ZipList<'a> * y: ZipList<'a> -> ZipList<'a> (requires member ``+``)
static member (<*>) : ZipList<('a -> 'b)> * ZipList<'a> -> ZipList<'b>
static member (<|>) : ZipList<'a1> * ZipList<'a1> -> ZipList<'a1>
static member Return: x: 'a -> ZipList<'a>
static member ToSeq: ZipList<'a1> -> 'a1 seq
static member Traverse: ZipList<'T> * f: ('T -> 'Functor<'U>) -> 'Functor<ZipList<'U>> (requires member Map and member ``<*>`` and member IsLeftZero and member Map and member Map and member Traverse and member Return and member Map)
static member get_Empty: unit -> ZipList<'a1>
static member Zero: unit -> ZipList<'a> (requires member Zero)
<summary>
A sequence with an Applicative functor based on zipping.
</summary>
Multiple items
val seq: sequence: 'T seq -> 'T seq
--------------------
type 'T seq = System.Collections.Generic.IEnumerable<'T>
val tryDiv: x: int -> y: int -> int option
val x: int
val y: int
union case Option.None: Option<'T>
val div: dividend: 'Num -> divisor: 'Num -> 'Num (requires member Divide)
<summary>Division between two numbers. If the numbers are not divisible throws an error.</summary>
<category index="22">Numerics</category>
val resSome3: int option
val join: x: 'Monad<Monad<'T>> -> 'Monad<'T> (requires member Join)
<summary>
Flattens two layers of monadic information into one.
</summary>
<category index="3">Monad</category>
val resSome3': int option
Multiple items
union case Ji.Ji: Ji
--------------------
type Ji = | Ji
val tryDivBy: y: int -> (int -> int) option
val resSome2: int option
val resSome2': int option
val resSome2'': int option
union case J.J: J
val resNone: int option
val res16n17: int list
val opt121: int option
val opt122: int option
val trySqrt: x: 'a -> 'a option (requires member TrySqrt)
<summary>Square root of a number of any type. Returns None if there is no square root.</summary>
<category index="22">Numerics</category>
namespace FSharpPlus.Math
module Applicative
from FSharpPlus.Math
<summary>Math Operators ready to use over Applicative Functors.</summary>
val opt121': int option
val optTrue: bool option
val optFalse: bool option
val m1m2m3: int list
val getName: s: string list -> string option
val s: string list
val getAge: s: string -> int option
val s: string
val person4: Person option
val applicative<'Applicative<'T>> : ApplicativeBuilder<'Applicative<'T>>
<summary>
Creates a (sequential) applicative computation expression.
</summary>
val name: string
val age: int
val res4: Compose<int option list>
Multiple items
union case Compose.Compose: 'functorF<'functorG<'t>> -> Compose<'functorF<'functorG<'t>>>
--------------------
module Compose
from FSharpPlus.Data
<summary>
Basic operations on Compose
</summary>
--------------------
[<Struct>]
type Compose<'functorF<'functorG<'t>>> =
| Compose of 'functorF<'functorG<'t>>
static member ( *> ) : x: 'FunctorF<'FunctorG<'T>> * y: 'FunctorF<'FunctorG<'U>> -> 'FunctorF<'FunctorG<'U>> (requires member Map and member ``<*>``)
static member (<!>) : f: ('T -> 'U) * x: 'FunctorF<'FunctorG<'T>> -> Compose<'FunctorF<'FunctorG<'U>>> (requires member Map and member Map)
static member ( <* ) : x: 'FunctorF<'FunctorG<'U>> * y: 'FunctorF<'FunctorG<'T>> -> 'FunctorF<'FunctorG<'U>> (requires member ``<*>`` and member Map)
static member (<*>) : Compose<'ApplicativeF<'ApplicativeG<'T -> 'U>> * Compose<'ApplicativeF<'ApplicativeG<'T>> -> Compose<'ApplicativeF<'ApplicativeG<'U>> (requires member Map and member ``<*>`` and member ``<*>``)
static member (<.>) : Compose<'ApplicativeF<'ApplicativeG<'T -> 'U>> * Compose<'ApplicativeF<'ApplicativeG<'T>> -> Compose<'ApplicativeF<'ApplicativeG<'U>> (requires member Map and member ``<.>`` and member ``<.>``)
static member (<|>) : Compose<'AlternativeF<'ApplicativeG<'T>> * Compose<'AlternativeF<'ApplicativeG<'T>> -> Compose<'AlternativeF<'ApplicativeG<'T>> (requires member ``<|>``)
static member Lift2: f: ('T -> 'U -> 'V) * Compose<'ApplicativeF<'ApplicativeG<'T>> * Compose<'ApplicativeF<'ApplicativeG<'U>> -> Compose<'ApplicativeF<'ApplicativeG<'V>> (requires member Lift2 and member Lift2)
static member Lift3: f: ('T -> 'U -> 'V -> 'W) * Compose<'ApplicativeF<'ApplicativeG<'T>> * Compose<'ApplicativeF<'ApplicativeG<'U>> * Compose<'ApplicativeF<'ApplicativeG<'V>> -> Compose<'ApplicativeF<'ApplicativeG<'W>> (requires member Lift3 and member Lift3)
static member Map: Compose<'FunctorF<'FunctorG<'T>>> * f: ('T -> 'U) -> Compose<'FunctorF<'FunctorG<'U>>> (requires member Map and member Map)
static member Map2: f: ('T -> 'U -> 'V) * Compose<'ApplicativeF<'ApplicativeG<'T>> * Compose<'ApplicativeF<'ApplicativeG<'U>> -> Compose<'ApplicativeF<'ApplicativeG<'V>> (requires member Map2 and member Map2)
...
<summary>
Right-to-left composition of functors. The composition of applicative functors is always applicative, but the composition of monads is not always a monad.
</summary>
val getNameAsync: s: string list -> Async<string option>
val async: AsyncBuilder
val getAgeAsync: s: string -> Async<int option>
val person5: Compose<Async<Person option>>
val person6: Async<Person option>
val applicative2<'Applicative1<Applicative2<'T>>> : ApplicativeBuilder2<'Applicative1<Applicative2<'T>>>
<summary>
Creates a (sequential) applicative computation expression which compose effects of two Applicatives.
</summary>
val printfn: format: Printf.TextWriterFormat<'T> -> 'T
's
Multiple items
union case MyList.MyList: 's seq -> MyList<'s>
--------------------
type MyList<'s> =
| MyList of 's seq
static member (>>=) : MyList<'T> * f: ('T -> MyList<'a>) -> MyList<'a>
static member Return: x: 'a -> MyList<'a>
val x: 'a
'a
Multiple items
module Seq
from FSharpPlus.Data
<summary>
Additional operations on Seq
</summary>
--------------------
module Seq
from FSharpPlus.Operators
--------------------
module Seq
from FSharpPlus
<summary>
Additional operations on Seq
</summary>
--------------------
module Seq
from Microsoft.FSharp.Collections
val singleton: value: 'T -> 'T seq
val x: 'T seq
'T
val f: ('T -> MyList<'a>)
val collect: mapping: ('T -> #('U seq)) -> source: 'T seq -> 'U seq
val x: 'a seq
val mappedMyList: MyList<int>