jaredramirez / border-types

A tool to keep you types in syncs across your stack
BSD 3-Clause "New" or "Revised" License
0 stars 0 forks source link

Type Definition Schema #4

Open jaredramirez opened 6 years ago

jaredramirez commented 6 years ago

How should the type definition config file look?

From https://www.notion.so/A-script-to-generate-both-Reason-and-Elm-types-for-ports-5177b1c921c74d55a05be797ef4a43bf but in JSON

{
    "<type-name>": {
        "<constructor-name1>": {
            "<field-name1>": "<field-type>"
        },
        "<constructor-name2>": {
            "<field-name2>": "<field-type>"
        },
    }
}

Would translate to:

-- ELM
type <type-name>
     = <constructor-name1> {<field-name1> : <field-type>}
     | <constructor-name2> {<field-name2> : <field-type>}
// REASON
type <type-name> =
    | <constructor-name1>({. "<field-name1>": <field-type>})
    | <constructor-name2>({. "<field-name2>": <field-type>})

This is all fine and good, however there are a few things that this does not address: a) Defining a top-level type alias (and the how to differentiate between unions & aliases) b) Defining arguments on union types that are not records c) Defining a top-level type OR type alias, and reference it later

jaredramirez commented 6 years ago

An alternate schema that would satisfy points a & b is:

[
    {
        "name": "<type-name>",
        "kind": "alias",
        "value": "<field-type>"
    },
    {
        "name": "<type-name>",
        "kind": "alias",
        "value": {
             "<field-name1>": "<field-type>"
             "<field-name2>": "<field-type>"
        }
    },
    {
        "name": "<type-name2>",
        "kind": "union",
        "constructors": {
             "<constructor-name1>": {
                 "<field-name1>": 
                     {
                         "name": "<type-name3>",
                         "kind": "alias",
                         "fields": {
                             "<sub-field-name1>": "<sub-field-type>"
                             "<sub-field-name2>": "<sub-field-type>"
                         }
                     },
             },
             "<constructor-name2>": {
                 "<field-name2>": "<field-type>"
            },
        }
    }
]

In this schema, <field-type> is any one of Int, Float, Bool, String or another alias or union declaration.

This does not address associations between types, but the fact that you can defined nested types can partially solve this.

@splodingsocks what are you thoughts on this schema?

jaredramirez commented 6 years ago

I an now thinking something like this

{
    "languages": [
        {
            "name": "elm",
            "outputPath": "./src/elm/Data",
            "elmFormatPath": "./path/to/elmFormat" // optional
        },
        {
            "name": "reason",
            "outputPath": "./src/reason/Data",
            "refmt": 3, // syntax version
            "refmtPath": "./path/to/refmt" // optional
        }
    ],
    "types": [
        {
            "name": "<type-name1>",
            "kind": "alias",
            "value": <primitive-type1>
        },
        {
            "name": "<type-name2>",
            "kind": "union",
            "constructors": {
                 "<constructor-name1>": [
                     <primitive-type2>,
                     <primitive-type3>
                 ],
                 "<constructor-name2>": [
                     <primitive-type4>
                ],
            }
        }
    ]
}

This would spit out

// ELM
type alias <type-name1> = <primitive-type1>

typo <type-name1>
    = <constructor-name1> <primitive-type2> <primitive-type3>
    | <constructor-name2> <primitive-type4>
// REASON
type <type-name1> = <primitive-type1>

type <type-name2> =
    | <constructor-name1>(<primitive-type2>, <primitive-type3>)
    | <constructor-name2>(<primitive-type4>)

In this example, <primitive-typeN> can be string, int, float, bool, list (represented as a list with 1 element), unit (represented as an empty list), an x-tuple (represented as a list with many elements), or a record (an object). A list would look like:

[<primative-type>]

A unit would look like:

[]

An x-tuple would look like:

[<primative-type>, <primative-type>, <primative-type>]

A record would look like:

{
    "<field1>" : <primative-type>,
    "<field2>" : <primative-type>
}

I don't like how similar lists & tuples are in this example, and am thinking of alternative ways to represent lists and tuple in json.

Adding a top level object also provides the ability to gather metadata about the type generation. In this example it's being used to specify the output paths but it can also be used to specify language version (elm 0.18 vs 0.19), and various other things as we think of them