FSharpPlus


Extensions

Extensions are what you probably expect: helper functions for existing types.

They are defined as modules with the same name as the types they operate on under the FSharpPlus namespace, so can be accessed via:

open FSharpPlus

Some functions are common across foldable types such as intercalate on List, Array and Seq, and others are common across wrapping containers, such as map, bind and apply on List, Array, and Seq, but also Option and Result.

Construction:

The singleton function is already defined for Seq, Array and List, but F#+ adds it for Enumerator:

To construct MonadError instances (Result or Choice) you can use result/throw:

It's also possible to construct by wrapping exception producing functions:

// throws "ArgumentException: The input sequence was empty."
let expectedSingleItem1 : int = List.exactlyOne []

// returns a Result.Error holding the exception as its value:
let expectedSingleItem2 : Result<int,exn> = Result.protect List.exactlyOne []

// ...or like typical try prefixed functions, treat exception as None
let expectedSingleItem3 : Option<int> = Option.protect List.exactlyOne []

// which might look like this:
let inline tryExactlyOne xs = Option.protect List.exactlyOne xs

Deconstruction (unwrapping):

Some extensions on Result are designed to behave like Option:

To deconstruct MonadError instances (Result or Choice) use:

Note that there is also the generic either operator function that works exactly the same as Result.either.

Also, see the generic function option that unwraps an Option in a similar way to either.

On Foldables

Foldables are the class of data structures that can be folded to a summary value. Most collections, or specifically 'foldable' instances implement these:

let a = ["Bob"; "Jane"] |> List.intersperse "and"
// vat a : string list = ["Bob"; "and"; "Jane"]

let b = "WooHoo" |> String.intersperse '-'
// val b : string = "W-o-o-H-o-o"
let c = [[1;2]; [3;4]] |> List.intercalate [-1;-2];;
// val c : int list = [1; 2; -1; -2; 3; 4]

let d = ["Woo"; "Hoo"] |> String.intercalate "--o.o--";;
// val d : string = "Woo--o.o--Hoo"

On Monad/Functor/Applicatives

Types that implement these will (typically) have these functions defined:

These can also be invoked from the generic functions without module prefix as per generic functions & operators.

Flatten:

Flatten can be used when a container has another container inside it:

Note that on traversable types like List, Array and Seq, FSharp Core uses the more common concat for flatten and so this naming is continued for Enumerable:

Partitioning:

Partitioning can be done by applying a separating function that produces a Choice:

let isEven x = (x % 2) = 0
let chooseEven x = if isEven x then Choice1Of2 x else Choice2Of2 x

let e = [1; 2; 3; 4] |> List.partitionMap chooseEven
// val e : int list * int list = ([2; 4], [1; 3])

Conversion functions:

F#+ adds functions to convert between Result, Choice and Option types.

These should be self explanatory, but be aware that sometimes they are 'lossy' usually when converting to Option:

// Convert a Result to an Option - effectively throws away error value // when present, by replacing with None

request |> validateRequest |> Option.ofResult

Going the other way is similar, but a value needs to be filled in for None:

let xs = ["some value"]
let firstElementOption = xs |> List.tryHead

// Convert an `Option` to a `Result` will use unit as the Error:
firstElementOption |> Option.toResult

// ...but you can specify an error value with Option.toResultWith:
firstElementOption |> Option.toResultWith "No Element"

Converting between Choice and Result is often useful:

let asyncChoice = anAsyncValue |> Async.Catch |> Async.map Result.ofChoice

The String type:

Collections / Traversable types:

Async and Tasks:

Option, Choice and Result types:

Extensions Methods (on existing types):

These are usable from C#

namespace FSharpPlus
val expectedSingleItem1 : int
Multiple items
val int : value:'T -> int (requires member op_Explicit)

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

--------------------
type int<'Measure> = int
Multiple items
module List

from FSharpPlus

--------------------
module List

from Microsoft.FSharp.Collections

--------------------
type List<'T> =
  | ( [] )
  | ( :: ) of Head: 'T * Tail: 'T list
    interface IReadOnlyList<'T>
    interface IReadOnlyCollection<'T>
    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
    ...
val exactlyOne : list:'T list -> 'T
val expectedSingleItem2 : Result<int,exn>
Multiple items
module Result

from FSharpPlus

--------------------
module Result

from Microsoft.FSharp.Core

--------------------
[<Struct>]
type Result<'T,'TError> =
  | Ok of ResultValue: 'T
  | Error of ErrorValue: 'TError
type exn = System.Exception
val protect : f:('T -> 'U) -> x:'T -> Result<'U,exn>
val expectedSingleItem3 : Option<int>
Multiple items
module Option

from FSharpPlus

--------------------
module Option

from Microsoft.FSharp.Core
val protect : f:('T -> 'U) -> x:'T -> 'U option
val tryExactlyOne : xs:'a list -> 'a option
val xs : 'a list
val a : string list
val intersperse : element:'T -> source:'T list -> 'T list
val b : string
Multiple items
module String

from FSharpPlus

--------------------
module String

from Microsoft.FSharp.Core
val intersperse : element:char -> source:string -> string
val c : int list
val intercalate : separator:'a list -> source:seq<'a list> -> 'a list
val d : string
val intercalate : separator:string -> source:seq<string> -> string
val isEven : x:int -> bool
val x : int
val chooseEven : x:int -> Choice<int,int>
union case Choice.Choice1Of2: 'T1 -> Choice<'T1,'T2>
union case Choice.Choice2Of2: 'T2 -> Choice<'T1,'T2>
val e : int list * int list
val partitionMap : mapping:('T -> Choice<'T1,'T2>) -> source:'T list -> 'T1 list * 'T2 list
val xs : string list
val firstElementOption : string option
val tryHead : list:'T list -> 'T option
val toResult : source:'T option -> Result<'T,unit>
val toResultWith : errorValue:'Error -> source:'T option -> Result<'T,'Error>