Skip to content

Commit 26f0b20

Browse files
committed
+ Free.hoist
1 parent 19f03d5 commit 26f0b20

File tree

2 files changed

+19
-1
lines changed

2 files changed

+19
-1
lines changed

src/FSharpPlus/Data/Free.fs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,21 @@ module Free =
8484
| Roll (x: ^``Functor<Free<'Functor<'T>,'T>>``) -> f (loop f <!> x)
8585
loop f x
8686

87-
/// Lift any Functor into a Free structure
87+
/// Lift any Functor into a Free structure.
8888
let inline liftF (x: '``Functor<'T>``) : Free<'``Functor<'T>``,'T> = Roll (Map.Invoke (Pure: 'T -> Free<'``Functor<'T>``,'T>) x : '``Functor<Free<'Functor<'T>,'T>>``)
8989

90+
/// Lift a natural transformation from functor F to functor G into a natural transformation from Free of F to Free of G.
91+
let inline hoist (f: ^``F<Free<'F<'T>, 'T>>`` -> ^``G<Free<'F<'T>, 'T>>``) (x: Free<'``F<'T>``, 'T>) : Free<'``G<'T>``, 'T> =
92+
let rec loop f x =
93+
if opaqueId false then
94+
let _: '``G<Free<'F<'T>, 'T>>`` = Map.Invoke Unchecked.defaultof<Free<'``G<'T>``, 'T> -> Free<'``F<'T>``, 'T>> Unchecked.defaultof<'``G<Free<'G<'T>, 'T>>``>
95+
let _: '``G<Free<'G<'T>, 'T>>`` = Map.Invoke Unchecked.defaultof<'T -> Free<'``G<'T>``, 'T>> Unchecked.defaultof<'``G<'T>``>
96+
()
97+
match run x with
98+
| Pure x -> Pure x
99+
| Roll (x: ^``F<Free<'F<'T>, 'T>>``) -> Roll (Map.Invoke (loop f: _ -> _) (f x) : ^``G<Free<'G<'T>, 'T>>``)
100+
loop f x
101+
90102

91103
type Free<'``functor<'t>``,'t> with
92104

tests/FSharpPlus.Tests/Free.fs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,3 +352,9 @@ module Lift3 =
352352
|> Identity.run
353353

354354
areStEqual result ("1", "2", "3")
355+
356+
[<Test>]
357+
let hoistFunction () =
358+
let x: Free<Result<int, string>, int> = Pure 4
359+
let y = Free.hoist Result.toOption x
360+
Assert.IsInstanceOf<Option<Free<option<int>, int>>> (Some y)

0 commit comments

Comments
 (0)