Would it be unreasonable as a language feature, to...
# getting-started
s
Would it be unreasonable as a language feature, to give compiler hints on required fields to be set in type-safe builders? Also promotion of type-safe builder relevant properties, in the editor?
More info: • Say I have a type-safe builder that builds an object, with some properties, foo and bar, could I mark
foo
as required, so the IDE will hint, or even error, saying the value must be set? • Also, when you use autocomplete in the context of a type-safe builder, it feels like the direct properties are hidden amongst the list of other properties, when I feel like they should be higher priority.
Like here for example
input
is so far down the list, IMO it should be right at the top.
j
If it's required, why not make it a parameter of the builder function in the first place?
2
s
Is definitely a succinct and readable solution, but I kinda like keeping everything in the builder specifically
Copy code
binding {
        control = reference("control/CastSpell")
        input = key(<http://Input.Keys.SPACE|Input.Keys.SPACE>)
    }

     vs

    binding(reference("control/CastSpell", key(<http://Input.Keys.SPACE|Input.Keys.SPACE>)))
h
You can also add the parameter names and add new lines
2
With a function, you also get a compiler error if you add a required parameter in the future.
Also, how should it work with a custom config lambda?
Copy code
kotlin
fun foo(customConfig: Binding.() -> Unit) {
  binding {
    control = reference("control/CastSpell")
    input = key(Input.Keys.SPACE)
    customConfig()
  }
}
How could the IDE/compiler know which properties are required? It is only possible during runtime.
s
True
Fair enough
Modified DSL to this now, looks good!
d
The topic of more safe builders is on the table of our language design team. There were experiments regarding this feature based on contracts:
Copy code
fun build(init : XYZBuilder.() -> Unit): XYZ {
    contract {
        callsInPlace(init, InvocationKind.EXACTLY_ONCE)
        expectsTo(init, CallKind(XYZBuilder::setValX, InvocationKind.EXACTLY_ONCE, receiverOf(init)))
        expectsTo(init, CallKind(XYZBuilder::setVarY, InvocationKind.AT_LEAST_ONCE, receiverOf(init)))
        expectsTo(init, CallKind(XYZBuilder::setDefaultValZ, InvocationKind.AT_MOST_ONCE, receiverOf(init)))
    }

    val xyzBuilder = XYZBuilder()
    xyzBuilder.init()
    return xyzBuilder.create()
}
The prototype was promising, but it was decided to pause work on it until we get a proper picture what actually contracts are in the language.
👀 1