dn-m / MusicXML

Implementation of the musicXML specification in Swift
MIT License
74 stars 20 forks source link

[API] Initializers for `value`-holding types #103

Closed jsbean closed 5 years ago

jsbean commented 5 years ago

There are many types which have properties named value, which are generally "Simple Types", and are what XMLCoder calls "Coding Key Value Intrinsic". In XML parlance, complex types "extend" "Simple Types" with attributes.

For these types, we can remove the attribute label in initializers, as these types are required and usually self-explanatory.

For example, the complex type stem "extends" the stem-value type.

I suggest we implement initializers for types like this:

extension Stem {
    init(_ value: StemValue, ...) {
        self.value = value
        ...
    }
}

Which make the call site look something like this:

let stem = Stem(.up)

@DJBen, does this work for or against any of your thoughts on the matter?

DJBen commented 5 years ago

I think this makes sense and I agree. We just want to make sure this case applies to simple types only.

jsbean commented 5 years ago

I think this methodology could also be extended to types which are inherently attributed arrays of a union type (i.e., Kind) (e.g., Notations, Ornaments, Dynamics, Technical, Encoding, etc.).

Thus, we could have:

let notations = Notations([.ornaments(Ornaments([.mordent(Mordent())]))])

Instead of:

let notations = Notations(
    values: [
        .ornaments(
            Ornaments(
                values: [
                    .mordent(Mordent())
                ]
            )
        )
    ]
)

Furthermore, we could make these types conform to ArrayLiteralConvertible if they are simple cases like so (without attributes).

let notations = Notations([.ornaments([.mordent(Mordent()))])
jsbean commented 5 years ago

And to really stretch the ergonomics, we may be able to get away with defining static properties on top of these union types:

extension Ornament {
    static let mordent = Ornament.mordent(Mordent())
}

Thus making the previous example:

let notations = Notations([.ornaments([.mordent])])

Not entirely certain that the compiler will appreciate this, but it could be worth a try.

DJBen commented 5 years ago

And to really stretch the ergonomics, we may be able to get away with defining static properties on top of these union types:

extension Ornament {
    static let mordent = Ornament.mordent(Mordent())
}

Thus making the previous example:

let notations = Notations([.ornaments([.mordent])])

Not entirely certain that the compiler will appreciate this, but it could be worth a try.

We can already specify default values to the enums, like

enum Ornament {
    case mordent(Mordent = Mordent())
}
jsbean commented 5 years ago

Wait, whoa, what?! When did that happen? I'm gonna give that a shot.

jsbean commented 5 years ago

That's wild. The only slightly noisy aspect is that it has to be called as .mordent() rather than .mordent, but that is very interesting.