Proposal: Encode syntax
I think we can make writing JSON programaticaly a little more convenient without touching the core abstractions.
Before:
Encode.object
[
"foo",
Encode.object
[
"bar", Encode.list [ Encode.int 123; Encode.string "abc" ]
]
]
After:
json {
"foo" <~
json {
"bar" <~ json {
123
"abc"
}
}
}
{
"foo": {
"bar": [
123,
"abc"
]
}
}
Happy to riff on the exact syntax.
And the implementation (could not doubt be more efficient):
#r "nuget: Thoth.Json.Core"
open Thoth.Json.Core
type JsonValueHelper =
static member inline ($) (_ : JsonValueHelper, value : IEncodable) =
value
static member inline ($) (_ : JsonValueHelper, value : string) =
Encode.string value
static member inline ($) (_ : JsonValueHelper, value : int) =
Encode.int value
static member inline ($) (_ : JsonValueHelper, value : int64) =
Encode.int64 value
static member inline ($) (_ : JsonValueHelper, value : bool) =
Encode.bool value
// etc...
let inline toJsonValue value : IEncodable =
Unchecked.defaultof<JsonValueHelper> $ value
type JsonProperty = string * IEncodable
type JsonBuilder() =
membe this.Yield(prop : JsonProperty) =
Seq.singleton prop
member inline this.Yield(value : _) =
Seq.singleton (toJsonValue value)
member this.YieldFrom(props : JsonProperty seq) =
props
member this.YieldFrom(xs : IEncodable seq) =
xs
member this.Combine(xs : JsonProperty seq, ys) =
Seq.append xs ys
member this.Combine(xs : IEncodable seq, ys) =
Seq.append xs ys
member this.Zero() =
Seq.empty
member this.Delay(f) =
f ()
member this.For(seq : seq<'a>, mapFunction : 'a -> seq<'b>) =
seq
|> Seq.collect mapFunction
member this.Run(props : JsonProperty seq) : IEncodable =
props
|> Seq.toList
|> Encode.object
member this.Run(value : IEncodable) : IEncodable =
value
member this.Run(elements : IEncodable seq) : IEncodable =
elements
|> Seq.toList
|> Encode.list
let json = JsonBuilder()
let inline (<~) (name : string) (value : _) : JsonProperty =
name, toJsonValue value
This seems interesting, we could expose it either via a new package or via a module in Thoth.Core.
I think the module version would be good enough so people can "opt-in" on the feature if they like it.
I have remarks coming to my mind when seeing this notation:
<~is not always easy on some keyboard layout- I can't seem to be able to find a Fantomas settings allowing to keep the code formatted as
json {
"foo" <~
json {
"bar" <~ json {
123
"abc"
}
}
}
it seems to always format it as
json {
"foo"
<~ json {
"bar"
<~ json {
123
"abc"
}
}
}
Love it! How does <-- work with fantomas?
All the infix gives the same formatting except .. but this one an invalid syntax 😅