FSharpPlus


Bifoldable

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.

Minimal complete definition

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

Other operations

static member Bisum (x:Bifunctor<'T,'T>) :'T

Rules

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

Related Abstractions

Concrete implementations

From .Net/F#

From F#+

Examples

#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)