mlhaufe/brevity

Change `data` to no longer combine factory and variant declaration

Opened this issue · 3 comments

data currently defines a class hierarchy as well as a factory for constructing instances of that hierarchy:

const colorData = data({ Red: {}, Green: {}, Blue: {} })

const PointData = data({
    Point2: {x: Number, y: Number},
    Point3: {x: Number, y: Number, z: Number}
})

const PeanoData = data(() => ({
    Zero: {},
    Succ: {pred: PeanoData}
}))

const ListData = data((T) => ({
    Nil: {},
    Cons: {head: T, tail: ListData(T) }
}))

This requires extra effort in type checking, complection, and declaration (due to the fixpoint requirement).

If data did not declare a factory then the above forms can be represented as:

const ColorData = data({}),
    Red = data(ColorData, {}),
    Green = data(ColorData, {})

const PointData = data({}),
    Point2 = data(PointData, {x: Number, y: Number}),
    Point3 = data(PointData, {x: Number, y: Number})

const PeanoData = data({}),
    Zero = data(PeanoData, {}),
    Succ = data(PeanoData, {pred: PeanoData})

const ListData = (T) => {
    const ListData = data({ of: T }),
        Nil = data(ListData, {}),
        Cons = data(ListData, { head: T, tail: ListData })
    return List
}

The syntactic burden is comparable to the original forms except for the parameterized recursive form.

For complect to work as desired, each data declaration has a [children] symbol that returns the extensions:

Peano[children] // [Zero, Succ]

Which enables:

const Peano = complect(PeanoData, [...])

This should have the added benefit of making type declarations easier as they are non-trivial in typescript and very challenging to express in jsdoc.

The parameterized recursive form will cause an issue with strict equality tests:

const NumListData1 = ListData(Number),
    NumListData2 = ListData(Number)

NumListData1 !== NumListData2
const ListData = memofix((T) => {
    const _ListData = data({ of: T }),
        Nil = data(_ListData, {}),
        Cons = data(_ListData, { head: T, tail: ListData(T) })
    return _ListData
})

To derive a general solution I don't see how the splitting out of data to one per class helps. The recursive form implies that an owning object needs to be returned. Add that to the requirement of a [children] reference and it looks like we've come full-circle back to a factory-like object. abstracting the memofix form and generalizing for the family looks exactly like the original data decl with a semantic distinction that doesn't seem to make much of a difference...