CLOVIS
03/21/2025, 9:15 PMJoffrey
03/21/2025, 9:28 PMAndrew O'Hara
03/21/2025, 10:18 PMDan Rusu
03/22/2025, 4:31 AMzsmb
03/22/2025, 12:08 PMDan Rusu
03/23/2025, 1:48 AMDmitry Khalanskiy [JB]
03/24/2025, 8:56 AMUser::score set cond { User::role eq User::candidate }
.then { 1 }
.else { User::score add 1 }
is certainly readable, no question. However, there is too high of a potential to misunderstand the API (by being unattentive) or just mistype it:
user.score set cond { user.role eq user.candidate }
.then { 1 }
.else { user.score add 1 }
It seems these will have completely different behaviors, right?
If so, the verbosity could be a good thing if instead of of
, some name is chosen that accurately explains what will happen. Instead of relying on a bunch of easily ignored dots and colons to distinguish the behaviors, you'd have your DSL tell the whole story.
Maybe it would also be worth splitting the DSL into several separate ones: one that would only work with T
, the other would only work with Field<T>
, etc. Depends on if it's reasonable to mix-and-match several types of sources of data, or if it indicates a mistake.CLOVIS
03/24/2025, 10:20 AM.else { User::score add 1 }
> .else { user.score add 1 }
> It seems these will have completely different behaviors, right?
That's correct. The first one adds 1 to the value of the field score
as it is stored in the database, whereas the second one adds 1 to the regular Kotlin score
variable.
Indeed, the DSL could be modified to be:
.else { field(User::score) add of(1) }
and
.else { of(user.score) add of(1) }
which wouldn't compile for the incorrect case.
However, I don't really know how much of a possible error that is. If the user had fields minScore
and maxScore
, they could also type the wrong field name and assign the wrong field, but I feel like that's a basic programming problem, the user could always refer to the wrong field. I'd have to do user-research to know if it's a mistake that actually happens or not.
> Maybe it would also be worth splitting the DSL into several separate ones: one that would only work with T
, the other would only work with Field<T>
, etc. Depends on if it's reasonable to mix-and-match several types of sources of data, or if it indicates a mistake.
In this case, there really is a single DSL. This is a DSL for MongoDB, and MongoDB is typed dynamically, so in "real" requests you can put whatever there. In my DSL, I encode the different kinds of things you can put there as different types. Ideally, I'd have a supertype of "things you can put in a DSL", but two of the four things you can put there (KProperty1
and "just a regular Kotlin variable") can't be part of custom type hierarchies at the moment. With unions (or "unions-as-an-implicit-conversion" that I mentioned in one of the threads above), I could create such a supertype, and thus the entire reason for having overloads goes away.Dmitry Khalanskiy [JB]
03/24/2025, 10:57 AMthey could also type the wrong field name and assign the wrong fieldThe main differences are that • `minScore`/`maxScore` copy-paste errors are indeed unavoidable, but as the library designer, you can avoid introducing the same kind of issue to your DSL! No need to give up just because the world isn't perfect. • You can always detect `minScore`/`maxScore` by reading your code carefully. If you don't fully understand how the DSL works, no amount of re-reading your code will help, you'll need to refer to the documentation/Stack Overflow.
CLOVIS
03/24/2025, 1:49 PMof()
in every single usage. Though that's my current approach because there aren't any alternatives at the moment.louiscad
04/03/2025, 9:32 AMCLOVIS
04/03/2025, 11:43 AMT
• KProperty1<*, T>
• Field<*, T>
• Value<*, T>
I could create a common interface between Field<*, T>
and Value<*, T>
but it makes a few things more complex. However, I can't create a supertype to Any
, which would be required to have T
be part of the hierarchy