Rob Elliot
02/03/2023, 11:25 AMStephan Schröder
02/05/2023, 8:43 AMproperties.apply { put(prop.name, initialValue) }
instead of the way easier to read
properties.put(prop.name, initialValue)
??Sam
02/05/2023, 8:45 AMapply
lets you modify and return it in a single line. As usual there's definitely a trade-off between succinctness and readability though 👍 two lines might be easier to understandStephan Schröder
02/05/2023, 8:52 AMproperty
is implicit 😆Rob Elliot
02/05/2023, 1:33 PMclass Props : AbstractPropertyMap<Any>() {
val anInt by property(1)
}
anInt
has compile time type Any
. I couldn't find a way to make its type defined by the type of the argument to property
.class Props : AbstractPropertyMap<Any>() {
val notAnInt: Int by property("a string")
}
compiles (I don't understand why! properties.getValue("a string")
returns Any
...) but fails at runtime with the inevitable ClassCastException
when you dereference notAnInt
.
Oddly you can call Props().entries
happily, and it will contain "notAnInt" to "a string"
so the type of notAnInt
must be late bound despite the by property
being evaluated on construction.V
on AbstractPropertyMap
has to be invariant despite AbstractPropertyMap
being effectively immutable, because V
is both a parameter to protected fun property
and a return type. It would have been nice to make it out V
as from a client's perspective it could be.)@UnsafeVariance
🙂Sam
02/05/2023, 3:18 PMMap<String, V>
as the property delegate, meaning the property type is always just V
, never a subtype. Not very useful if V
is Any
. I think it can be improved like this, though:
abstract class AbstractPropertyMap<V>(
private val properties: MutableMap<String, V> = mutableMapOf()
) : Map<String, V> by properties {
protected fun <V2: V> property(initialValue: V2) =
PropertyDelegateProvider<Any, ReadOnlyProperty<Any, V2>> { _, prop ->
properties[prop.name] = initialValue
ReadOnlyProperty(properties::getValue)
}
}
Map
as a property delegate that provides a specific type (V2
) for whichever property it's currently trying to provide.@UnsafeVariance
to get the out V
variance, which would make it protected fun <V2: @UnsafeVariance V> property(initialValue: V2)
... and I've got to be honest, I'm not entirely sure what that means 😅Rob Elliot
02/05/2023, 10:40 PMMatteo Mirk
03/14/2023, 2:19 PMRob Elliot
03/15/2023, 3:27 PMMatteo Mirk
03/16/2023, 2:32 PMget(Symbol<T>): T
put(Symbol<T>, T): Unit
Rob Elliot
03/16/2023, 2:56 PMclass Foo: AbstractPropertyMap<Any>() {
val prop1 by property("value1")
val prop2 by property(21)
}
The two things seem to have different goals to me; SymbolMap is about having a Map<Symbol<Any>, Any>
where you can be sure you put and retrieve a specific type. AbstractPropertyMap
is about having something which is both a Map<String, T>
and an object such that the fields on the object are also entries in the Map with the same name and value.val symbolMap: SymbolMap = map().put($bool, boolValue).put($int, intValue).put($null, null).solid()
you cannot do `symbolMap.bool`; you'd have to wrap it in a class with a val bool: Boolean = symbolMap[$bool]
field, wouldn't you? At which point you've repeated bool
multiple times, which is what I was trying to avoid.Matteo Mirk
03/16/2023, 3:06 PM