Skip to content

Commit 5a909e0

Browse files
authored
feat: add Decode.fix allowing to decode recursive functions
1 parent 8bacd71 commit 5a909e0

File tree

2 files changed

+75
-0
lines changed

2 files changed

+75
-0
lines changed

packages/Thoth.Json.Core/Decode.fs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1434,6 +1434,42 @@ module Decode =
14341434
=
14351435
map Map.ofSeq (array (tuple2 keyDecoder valueDecoder))
14361436

1437+
type private FixDecoder<'a>(make: Decoder<'a> -> Decoder<'a>) as this =
1438+
let self = make this
1439+
1440+
interface Decoder<'a> with
1441+
member this.Decode
1442+
(helpers: IDecoderHelpers<'JsonValue>, value: 'JsonValue)
1443+
=
1444+
self.Decode(helpers, value)
1445+
1446+
/// <summary>
1447+
/// Allow to build a decoder that can call itself
1448+
/// </summary>
1449+
/// <example>
1450+
/// <code lang="fsharp">
1451+
/// type Tree =
1452+
/// | Empty
1453+
/// | Branch of Tree * int * Tree
1454+
///
1455+
/// module Tree =
1456+
///
1457+
/// let decode =
1458+
/// Decode.fix
1459+
/// (fun self ->
1460+
/// Decode.oneOf
1461+
/// [
1462+
/// Decode.unit
1463+
/// |> Decode.map (fun () -> Tree.Empty)
1464+
///
1465+
/// Decode.tuple3 self Decode.int self
1466+
/// |> Decode.map Tree.Branch
1467+
/// ])
1468+
/// </code>
1469+
/// </example>
1470+
let fix (make: Decoder<'a> -> Decoder<'a>) : Decoder<'a> =
1471+
FixDecoder<'a>(make)
1472+
14371473
////////////
14381474
// Enum ///
14391475
/////////

tests/Thoth.Json.Tests/Decoders.fs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ type UnionWithPrivateConstructor =
4646

4747
type UnionWithMultipleFields = | Multi of string * int * float
4848

49+
type Tree<'a> =
50+
| Empty
51+
| Branch of Tree<'a> * 'a * Tree<'a>
52+
4953
let tests (runner: TestRunner<'DecoderJsonValue, 'EncoderJsonValue>) =
5054
testList
5155
"Thoth.Json.Decode"
@@ -2829,6 +2833,41 @@ Expecting an object with a field named `version` but instead got:
28292833
let actual = runner.Decode.fromString decodeAll "{}"
28302834

28312835
equal expected actual
2836+
2837+
2838+
testCase "fix works"
2839+
<| fun _ ->
2840+
let expected =
2841+
Ok(
2842+
Tree.Branch(
2843+
Tree.Empty,
2844+
3,
2845+
Tree.Branch(
2846+
Tree.Branch(Tree.Empty, 5, Tree.Empty),
2847+
4,
2848+
Tree.Empty
2849+
)
2850+
)
2851+
)
2852+
2853+
let decoder =
2854+
Decode.fix (fun self ->
2855+
Decode.oneOf
2856+
[
2857+
Decode.unit
2858+
|> Decode.map (fun () -> Tree.Empty)
2859+
2860+
Decode.tuple3 self Decode.int self
2861+
|> Decode.map Tree.Branch
2862+
]
2863+
)
2864+
2865+
let actual =
2866+
runner.Decode.fromString
2867+
decoder
2868+
"[ null, 3, [ [ null, 5, null ], 4, null ]]"
2869+
2870+
equal expected actual
28322871
]
28332872

28342873
testList

0 commit comments

Comments
 (0)