Skip to content

Proposal: Encode syntax #225

@njlr

Description

@njlr

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions