Is there possibility with dsl construct copy. Some...
# compiler
e
Is there possibility with dsl construct copy. Something like
Copy code
data class Wrapper(
   val data: String = "Test",
   val intData: Int = 5
)

fun wrapper(copy: Wrapper.() -> Wrapper) {
  Wrapper().copy(....)
}

val wrapper = wrapper { data = "Another" } // Constructs Wrapper("Another", 5)
d
Why not just
copy(data = "Another")
e
Yeah, sorry a not perfect example. Let me update it.
d
You might be interested in optics from #C5UPMM0A0 https://arrow-kt.io/learn/immutable-data/intro/
e
Yeah, I'm looking for something like optics, but this is compiler plugin
And I don't want to get it in project right now for one this case
Ah, it is even not compiler plugin but annotation processor
d
It's not that hard to integrate FWIW.
e
It is not about hardness
But oke, optics are also not a thingy that I would like to achieve. I want to avoid typing
copy
function in dsl usage
Copy code
wrapper {
  data = "Another"
}
y
Do you have a more concrete example? I'm confused as to what syntax you want exactly and what you want it to achieve.
d
It seems like you just want a builder with defaults: fun
wrapper(data: String = "Test",  intData: Int = 5) = Wrapper(data, intData)
If there is some other reason you think a lambda would be useful, maybe that's the part you're caught on?
e
I don't want to have builder, I want to have reference of the field in the original class. Otherwise, I have map one to one, means a lot of duplication (these data class has 30 fields), I need to remember update builder if a new field is added to data class, it will not shown in the usage, etc.
d
So you want a mutator?
e
yup
So I could achieve it if I would make fields in data class as mutable
d
Yeah, that seems like the only approach that doesn't require a bunch of duplication.
e
@Youssef Shoaib [MOD], we have a set of feature toggles, they are wrapped in fake settings data class for tests. I want to have a dsl for test that allows mutate one or couple of settings with keeping other defaults. So I have already in test something like
FakeSettings(feature X = true)
and I was thinking if I could have dsl to constructor call.
d
It's a tradeoff. Do you want immutability or ease of use.
e
Haha, I want some elegant solution
y
So you basically want:
Copy code
fakeSettings {
  featureX = true
}
Right? Here's a somewhat cool idea. How about forgetting that data class, and instead using a Set? Example incoming...
Copy code
enum class Setting {
  FeatureX
  FeatureY
  FeatureZ
  // As many as you want!
}
typealias Settings = Set<Setting>
class SettingsBuilder(private val underlying: MutableSet<Setting>) {
  operator fun Setting.unaryPlus() {
    underlying.add(this)
  }
  operator fun Setting.unaryMinus() {
    underlying.remove(this)
  }
  val Setting.enabled get() = this in underlying
}
val defaultSettings: Settings = setOf() // add your default settings here

inline fun settings(block: SettingsBuilder.() -> Unit): Settings = buildSet {
  addAll(defaultSettings)
  SettingsBuilder(this).block()
}
// Usage
settings {
  +FeatureX
  -FeatureY
  if(FeatureZ.enabled) -FeatureX
}
Way easier to maintain!
FeatureX in settings
also works out of the box because
Settings
is just a Set. You can even not have an enum and instead allow creation of arbitrary features if you really wanted. Bulk operations can be easily added as well.
EnumSet
from Java can be used here if you're somehow worried about efficiency
e
Nice!
For unknow reasons, the feature toggles here are not set but fields in the interface. Which require update classes if new feature is created.
I'm not going to take it now
But maybe in the future
Thanks!
y
You could even slowly migrate that. If you want to go the updating-a-data-class route, you can use optics I think. Just wrap it up in a nice and simple DSL that encapsulates your default settings.
Migrating might not even be that bad. Simply make the properties use
.enabled
on get and
+
and
-
on set. Then you can easily use the inline refactor in Idea whenever you want to simplify such code.