Go to file
Patrick Stevens f1304df21d NuGet Pack (#15)
2023-12-27 22:47:35 +00:00
2023-12-27 22:47:35 +00:00
2023-12-27 22:47:35 +00:00
2023-12-27 22:47:35 +00:00
2023-12-27 22:47:35 +00:00
2023-05-06 15:48:36 +01:00
2023-12-27 22:47:35 +00:00
2023-12-27 22:47:35 +00:00
2023-05-06 15:48:36 +01:00
2023-05-06 15:48:36 +01:00
2023-12-27 22:47:35 +00:00
2023-12-27 22:47:35 +00:00
2023-12-27 22:47:35 +00:00
2023-12-24 12:43:01 +00:00
2023-12-27 22:47:35 +00:00
2023-12-24 12:43:01 +00:00
2023-05-06 15:48:36 +01:00
2023-12-27 22:47:35 +00:00
2023-12-27 20:57:16 +00:00

WoofWare.Myriad.Plugins

Some helpers in Myriad which might be useful.

These are currently somewhat experimental, and I personally am their primary customer. The RemoveOptions generator in particular is extremely half-baked.

Currently implemented:

  • JsonParse (to stamp out jsonParse : JsonNode -> 'T methods);
  • RemoveOptions (to strip option modifiers from a type).

JsonParse

Takes records like this:

[<MyriadPlugin.JsonParse>]
type InnerType =
    {
        [<JsonPropertyName "something">]
        Thing : string
    }

/// My whatnot
[<MyriadPlugin.JsonParse>]
type JsonRecordType =
    {
        /// A thing!
        A : int
        /// Another thing!
        B : string
        [<System.Text.Json.Serialization.JsonPropertyName "hi">]
        C : int list
        D : InnerType
    }

and stamps out parsing methods like this:


/// Module containing JSON parsing methods for the InnerType type
[<RequireQualifiedAccess>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module InnerType =
    /// Parse from a JSON node.
    let jsonParse (node: System.Text.Json.Nodes.JsonNode) : InnerType =
        let Thing = node.["something"].AsValue().GetValue<string>()
        { Thing = Thing }
namespace UsePlugin

/// Module containing JSON parsing methods for the JsonRecordType type
[<RequireQualifiedAccess>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module JsonRecordType =
    /// Parse from a JSON node.
    let jsonParse (node: System.Text.Json.Nodes.JsonNode) : JsonRecordType =
        let D = InnerType.jsonParse node.["d"]

        let C =
            node.["hi"].AsArray() |> Seq.map (fun elt -> elt.GetValue<int>()) |> List.ofSeq

        let B = node.["b"].AsValue().GetValue<string>()
        let A = node.["a"].AsValue().GetValue<int>()
        { A = A; B = B; C = C; D = D }

What's the point?

System.Text.Json, in a PublishAot context, relies on C# source generators. The default reflection-heavy implementations have the necessary code trimmed away, and result in a runtime exception. But C# source generators are entirely unsupported in F#.

This Myriad generator expects you to use System.Text.Json to construct a JsonNode, and then the generator takes over to construct a strongly-typed object.

Limitations

This source generator is enough for what I first wanted to use it for. However, there is far more that could be done.

  • Make it possible to give an exact format and cultural info in date and time parsing.
  • Make it possible to reject parsing if extra fields are present.
  • Rather than just throwing NullReferenceException, print out the field name that failed.
  • Generally support all the System.Text.Json attributes.

RemoveOptions

Takes a record like this:

type Foo =
    {
        A : int option
        B : string
        C : float list
    }

and stamps out a record like this:

[<RequireQualifiedAccess>]
module Foo =
    type Short =
        {
            A : int
            B : string
            C : float list
        }

What's the point?

The motivating example is argument parsing. An argument parser naturally wants to express "the user did not supply this, so I will provide a default". But it's not a very ergonomic experience for the programmer to deal with all these options, so this Myriad generator stamps out a type without any options, and also stamps out an appropriate constructor function.

Limitations

This generator is far from where I want it, because I haven't really spent any time on it.

  • It really wants to be able to recurse into the types within the record, to strip options from them.
  • It needs some sort of attribute to mark a field as not receiving this treatment.
  • What do we do about discriminated unions?

Detailed examples

See the tests. For example, PureGymDto.fs is a real-world set of DTOs.

Description
Some Myriad plugins for F#
Readme MIT 3.4 MiB
Languages
F# 99.8%
Nix 0.2%