Intuitively a bifoldable is a type with 2 arguments, each of them being foldable.
A bifoldable (short for binary foldable) or foldable of two variables is a container of up to two elements whose components can be folded to a single value.
Unlike bimap which preserves the container type, bifoldable will extract and fold the value.
static member BifoldMap (x:'Bifoldable<'T,'V>, f:'T->'U, g:'V->'U) :'U
static member Bifold (x:'Bifoldable<'T,'V>, f:'T->'Monoid, g:'V->'Monoid, z: 'Monoid) :'Monoid
static member BifoldBack (x:'Bifoldable<'T,'V>, f:'T->'Monoid, g:'V->'Monoid, z: 'Monoid) :'Monoid
static member Bisum (x:Bifunctor<'T,'T>) :'T
bisum x = bifoldMap id id x
bifoldMap f g x = bifoldBack (f >> (++)) (g >> (++)) x zero
bifoldBack f g x z = Endo.run (bifoldMap (f >> Endo) (g >> Endo) x) z
- Foldable: All bifoldable contain up to two elements that are foldable to a single common type.
- Monoid: For containers where the two elements are not disjoint, the same relation that foldable has to monoid applies.
From .Net/F#
'T * 'U
struct ('T * 'U)
Result<'T, 'U>
Choice<'T, 'U>
From F#+
#r @"../../src/FSharpPlus/bin/Release/netstandard2.0/FSharpPlus.dll"
open FSharpPlus
open FSharpPlus.Control
let listMapSeqLength = List.map Seq.length
let listMapTimes2 = List.map ((*) 2)
let c1 : Choice<int list,string list> = Choice1Of2 [1..2]
let c2 : Choice<int list,string list> = Choice2Of2 ["a";"bbbb"]
bifoldBack (listMapSeqLength >> (++)) (listMapTimes2 >> (++)) c1 [0] // = [2;4;0]
bifoldBack (listMapSeqLength >> (++)) (listMapTimes2 >> (++)) c2 [0] // = [1;4;0]
bifoldMap listMapSeqLength listMapTimes2 c1 // = [2;4]
bifoldMap listMapSeqLength listMapTimes2 c2 // = [1;4]
let t = ("b","c")
bifoldBack (++) (++) t "a" // = "bca"
bifold (++) (++) "a" t // = "abc"
// implementing on custom type:
type MyEither<'a,'b> =
| MyLeft of 'a
| MyRight of 'b
static member inline BifoldMap (x: MyEither<_,_>, f, g) =
match x with
| MyLeft a -> f a
| MyRight a -> g a
static member BifoldBack (x: MyEither<_,_>, f, g, z) =
match x with
| MyLeft a -> f a z
| MyRight a -> g a z
bisum (MyEither.MyLeft "a") // = "a"
bisum (1,2) // = 3
let inline law1 x =
bisum x = bifoldMap id id x
law1 (1,1) // = true
law1 (Ok [1;2;3]) // = true
law1 (Error [1;2;3]) // = true
law1 (Choice1Of2 [1;2;3]) // = true
law1 (Choice2Of2 [1;2;3]) // = true
law1 (MyLeft [1;2;3]) // = true
law1 (MyRight [1;2;3]) // = true
let inline law2 x f g =
bifoldMap f g x = bifoldBack (f >> (++)) (g >> (++)) x zero
law2 (1,1) ((+) 1) ((+) 2) // = true
law2 (Ok [1;2;3]) ((++) [1]) ((++) [2]) // = true
law2 ("a","b") ((+) "bbbb") ((+) "aaaa") // = true
open FSharpPlus.Data
let inline law3 x f g z =
bifoldBack f g x z = Endo.run (bifoldMap (f >> Endo) (g >> Endo) x) z
law3 (1,1) (++) (++) 5 // = true
law3 ("a","b") (++) (++) "abcd" // = true
law3 (Ok [1;2;3]) (++) (++) [0;1;2;3;4] // = true
val id: x: 'T -> 'T
namespace FSharpPlus
namespace FSharpPlus.Control
val listMapSeqLength: (string list -> int 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
Multiple items
module Seq
from FSharpPlus.Operators
--------------------
module Seq
from FSharpPlus
<summary>
Additional operations on Seq
</summary>
--------------------
module Seq
from Microsoft.FSharp.Collections
val length: source: 'T seq -> int
val listMapTimes2: (int list -> int list)
val c1: Choice<int list,string list>
Multiple items
module Choice
from FSharpPlus
<summary>
Additional operations on Choice
</summary>
--------------------
type Choice =
inherit Default1
static member Choice: x: 'Foldable<'Alternative<'T>> ref * _mthd: Default4 -> 'a1 (requires member ToSeq and member ``<|>`` and member IsAltLeftZero and member Empty) + 7 overloads
static member Invoke: x: 'Foldable<'Alternative<'T>> -> 'Alternative<'T>> (requires member Choice)
--------------------
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
type 'T list = List<'T>
Multiple items
val string: value: 'T -> string
--------------------
type string = System.String
union case Choice.Choice1Of2: 'T1 -> Choice<'T1,'T2>
val c2: Choice<int list,string list>
union case Choice.Choice2Of2: 'T2 -> Choice<'T1,'T2>
val bifoldBack: leftFolder: ('T1 -> 'State -> 'State) -> rightFolder: ('T2 -> 'State -> 'State) -> source: 'Bifoldable<'T1,'T2> -> state: 'State -> 'State (requires member BifoldBack)
<summary>
Combines the elements of a structure in a left associative manner.
</summary>
<category index="14">Bifoldable</category>
val bifoldMap: f: ('T1 -> 'Monoid) -> g: ('T2 -> 'Monoid) -> source: 'Bifoldable<'T1,'T2> -> 'Monoid (requires member BifoldMap)
<summary>
Combines the elements of a structure, given ways of mapping them to a Common Combinators monoid.
</summary>
<category index="14">Bifoldable</category>
val t: string * string
val bifold: leftFolder: ('State -> 'T1 -> 'State) -> rightFolder: ('State -> 'T2 -> 'State) -> state: 'State -> source: 'Bifoldable<'T1,'T2> -> 'State (requires member Bifold)
<summary>
Combines the elements of a structure in a right associative manner.
</summary>
<category index="14">Bifoldable</category>
'a
'b
union case MyEither.MyLeft: 'a -> MyEither<'a,'b>
union case MyEither.MyRight: 'b -> MyEither<'a,'b>
type BifoldMap =
inherit Default1
static member BifoldMap: x: Result<'T2,'T1> * f: ('T1 -> 'U) * g: ('T2 -> 'U) * _impl: BifoldMap -> 'U + 5 overloads
static member Invoke: f: ('T1 -> 'U) -> g: ('T2 -> 'U) -> source: 'Bifoldable<T1,T2> -> 'U (requires member BifoldMap)
static member InvokeOnInstance: f: ('T1 -> 'U) -> g: ('T2 -> 'U) -> source: 'Bifoldable<'T1,'T2> -> 'U (requires member BifoldMap)
val x: MyEither<'a,'b>
type MyEither<'a,'b> =
| MyLeft of 'a
| MyRight of 'b
static member BifoldBack<'a,'b,'c,'d> : x: MyEither<'a0,'b1> * f: ('a0 -> 'c -> 'd) * g: ('b1 -> 'c -> 'd) * z: 'c -> 'd
static member BifoldMap<'a,'b,'c> : x: MyEither<'a0,'b1> * f: ('a0 -> 'c) * g: ('b1 -> 'c) -> 'c
val f: ('a -> 'c)
val g: ('b -> 'c)
val a: 'a
val a: 'b
type BifoldBack =
inherit Default1
static member BifoldBack: x: Result<'T2,'T1> * f: ('T1 -> 'S -> 'S) * g: ('T2 -> 'S -> 'S) * z: 'S * _impl: BifoldBack -> 'S + 5 overloads
static member Invoke: f: ('T1 -> 'S -> 'S) -> g: ('T2 -> 'S -> 'S) -> z: 'S -> source: 'Bifoldable<'T1,'T2> -> 'S (requires member BifoldBack)
static member InvokeOnInstance: f: ('T1 -> 'S -> 'S) -> g: ('T2 -> 'S -> 'S) -> z: 'S -> source: 'Bifoldable<'T1,'T2> -> 'S (requires member BifoldBack)
val f: ('a -> 'c -> 'd)
val g: ('b -> 'c -> 'd)
val z: 'c
val bisum: source: 'Bifoldable<'Monoid,'Monoid> -> 'Monoid (requires member Bisum)
<summary>
Combines the elements of a structure using a monoid.
</summary>
<category index="14">Bifoldable</category>
val law1: x: 'a -> bool (requires member Bisum and member BifoldMap and equality)
val x: 'a (requires member Bisum and member BifoldMap and equality)
union case Result.Ok: ResultValue: 'T -> Result<'T,'TError>
union case Result.Error: ErrorValue: 'TError -> Result<'T,'TError>
val law2: x: 'a -> f: ('b -> 'c) -> g: ('d -> 'c) -> bool (requires member BifoldMap and member BifoldBack and member Zero and member ``+`` and equality)
val x: 'a (requires member BifoldMap and member BifoldBack and member Zero and member ``+`` and equality)
val f: ('b -> 'c) (requires member Zero and member ``+`` and equality)
val g: ('d -> 'c) (requires member Zero and member ``+`` and equality)
val zero<'Monoid (requires member Zero)> : 'Monoid (requires member Zero)
<summary>
A value that represents the 0 element of a Monoid.
</summary>
<category index="4">Monoid</category>
namespace FSharpPlus.Data
val law3: x: 'a -> f: ('b -> 'c -> 'c) -> g: ('d -> 'c -> 'c) -> z: 'c -> bool (requires member BifoldBack and member BifoldMap and equality)
val x: 'a (requires member BifoldBack and member BifoldMap and equality)
val f: ('b -> 'c -> 'c) (requires equality)
val g: ('d -> 'c -> 'c) (requires equality)
val z: 'c (requires equality)
Multiple items
union case Endo.Endo: ('t -> 't) -> Endo<'t>
--------------------
module Endo
from FSharpPlus.Data
<summary>
Basic operations on Endo
</summary>
--------------------
[<Struct>]
type Endo<'t> =
| Endo of ('t -> 't)
static member (+) : Endo<'T> * Endo<'T> -> Endo<'T>
static member Zero: unit -> Endo<'T>
<summary>
The monoid of endomorphisms under composition.
</summary>
val run: Endo<'T> -> ('T -> 'T)