Hey @sideeffffect,
Yes, that would work. However while metadata = #(#(name = "foo"))
is ok-ish, it’s still messier than I would like it to be. So here’s the idea I had to deal with this issue and allow the nice metadata = #(name = "foo")
thing:
We could add a feature to customize which object the #
stands for. To do this, you would add a type alias called #
to the companion object of a type (such as Optional
). That type alias takes the same number and kinds of type parameters as the type (Optional
) itself, and the right hand side of the type alias is the type that will be substituted for the #
placeholder.
In case it isn’t clear (it probably isn’t), I’ll give an example, namely the Optional
type. In a context where a value of type Optional[A]
is expected, you don’t want the #
character to stand for Optional
, you want it to stand for A
. So you add the type alias to the companion object:
object Optional[+A] {
type #[+A] = A
}
Now, when the compiler sees an expression like #(name = "foo")
in a position where type Optional[ObjectMeta]
is expected, it will check the Optional
companion object and see the #
type alias, which tells it that the #
symbol in that expression is supposed to stand for something other than Optional
, namely A
. In this case, A
is ObjectMeta
, so #(name = "foo")
becomes ObjectMeta(name = "foo")
rather than Optional(name = "foo")
. To keep things simple, and because this feature specifically solves a problem related to implicit conversions, I would not allow chaining these, because implicit conversions also don’t chain.
Now granted, this feels a bit bolted on But it does solve the problem, and it would be the kind of feature that not very many people would even need to know about. The authors of Optional
would add that line to their companion object, and the users of Optional
would probably not even realize it’s there because it just behaves the way you would expect it to.
The question is: is there any use case for this feature beyond Optional
? Because if there isn’t, then maybe it’s better to have a dedicated language feature for optional function parameters. I think that would be justifiable because the existing options all suck: With Option
, you need wrap Some
around the parameter when you do specify it. ZIO’s Optional
avoids that using implicit conversions, but those are kind of deprecated. You can simply set a default, but then you can’t tell if the parameter was supplied by the user or not. @lihaoyi uses null
defaults in some of his libraries to avoid the need for Some
wrapping, but ideally we’d be moving towards explicit null typing, and because the type A | Null
doesn’t have a companion object, it wouldn’t work with the #
syntax. So there might be room for a dedicated feature for optional parameters.
Perhaps we should just special case A | Null
types and treat them the same as non-nullable A
for the purposes of #
expressions. That might be a better idea because it’s simpler, but at the same time, it would give | Null
types a special status and thus encourage the use of null
, which maybe we don’t want to do. So overall, I like the “# type alias” idea best for now.
So much for my brain dump for today.