-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Redesign encoding of element DSL #180
Comments
One shortcoming of this encoding is that the appropriate types / docs for a particular attribute will be very unclear. |
I prototyped this out a bit further. Not sure about my proposed encoding above, but this works (and is simpler): //> using lib "org.typelevel::shapeless3-deriving:3.3.0"
//> using option "-Ykind-projector:underscores"
import shapeless3.deriving.K0
trait Modifier[E, M]
trait SetAttribute[A, V]
class Tag[E]:
def apply[M <: Tuple](modifiers: M)(using
K0.ProductInstances[Modifier[E, _], M]
): E = ???
class Attribute[A]:
def :=[V](value: V): SetAttribute[A, V] = ???
// common to both
def sharedAttr: Attribute["shared"] = ???
// only on foo
def fooAttr: Attribute["foo"] = ???
// only on bar
def barAttr: Attribute["bar"] = ???
// on foo and bar, but with different types
def fizzBuzzAttr: Attribute["fizzbuzz"] = ???
trait Element
given sharedForElement[E <: Element]: Modifier[E, SetAttribute["shared", String]] = ???
trait FooElement extends Element
given fooForFooElement[E <: FooElement]: Modifier[E, SetAttribute["foo", String]] = ???
given fizzBuzzForFooElement[E <: FooElement]: Modifier[E, SetAttribute["fizzbuzz", Boolean]] = ???
trait BarElement extends Element
given barForBarElement[E <: BarElement]: Modifier[E, SetAttribute["bar", String]] = ???
given fizzBuzzForBarlement[E <: BarElement]: Modifier[E, SetAttribute["fizzbuzz", Int]] = ???
def fooTag: Tag[FooElement] = ???
def barTag: Tag[BarElement] = ???
def demo =
fooTag(
sharedAttr := "hello",
fooAttr := "foo",
fizzBuzzAttr := true
)
barTag(
sharedAttr := "hello",
barAttr := "bar",
fizzBuzzAttr := 42
) |
Here's another variation, that uses ordinary //> using lib "org.typelevel::shapeless3-deriving:3.3.0"
//> using option "-Ykind-projector:underscores"
import shapeless3.deriving.K0
trait Modifier[E, M]
trait SetAttribute[A, V]
class Tag[E]:
def apply[M <: Tuple](modifiers: M)(using
K0.ProductInstances[Modifier[E, _], M]
): E = ???
extension [A <: Singleton](attr: A)
def :=[V](value: V): SetAttribute[A, V] = ???
trait Element
given sharedForElement[E <: Element]: Modifier[E, SetAttribute["shared", String]] = ???
trait FooElement extends Element
given fooForFooElement[E <: FooElement]: Modifier[E, SetAttribute["foo", String]] = ???
given fizzBuzzForFooElement[E <: FooElement]: Modifier[E, SetAttribute["fizzbuzz", Boolean]] = ???
trait BarElement extends Element
given barForBarElement[E <: BarElement]: Modifier[E, SetAttribute["bar", String]] = ???
given fizzBuzzForBarlement[E <: BarElement]: Modifier[E, SetAttribute["fizzbuzz", Int]] = ???
def fooTag: Tag[FooElement] = ???
def barTag: Tag[BarElement] = ???
def demo =
fooTag(
"shared" := "hello",
"foo" := "foo",
"fizzbuzz" := true
)
barTag(
"shared" := "hello",
"bar" := "bar",
"fizzbuzz" := 42
) I'm not sure yet how I feel about it. Cons
Pros
Probably in the end, will support both styles to some extent 🤔 but this could also be highly confusing. |
Pick one for your own and everyone's sake 😁 looks like a nice way to go IMHO |
I vote for the non-string props version |
@hejfelix btw one downside of this proposed change, is that attributes will no longer be |
Yeah, that would suck. What would the workaround look like? edit nonetheless it seems like the right way going forward |
Actually, let me get back to you on this. In my experiments above, I only played out the I still need to try If type inference doesn't work, then we'd have to continue coding the specific And maybe this is okay, clashes between |
Yeah I recall pleasing the type inference for the mods is a pain. Very happy that this project exists ❤️ thanks for working hard on this! |
Ok, I have a new iteration on this idea that I think supports everything we want to do here (and possibly more), without reneging on the The idea is basically to re-encode tags to inject its type as an invisible context. Then all attributes within the tag can use that for inference. class Tag[E]:
def apply[M <: Tuple](modifiers: TagContext[E] ?=> M)(using
K0.ProductInstances[Modifier[E, _], M]
): E = ??? |
Context:
What we have now is pretty good, thanks to Laminar's influence. But I feel now that it is wrong, and we can do better. This should also open doors to better support web components and stuff.
Basically, I believe we need only two types: tags and attributes.
Tags should be similar to our existing encoding. Something like this:
Then, for attributes, we should have something like this:
What is
Setter[F, E, A, V]
? It is evidence, that you are allowed to set a value of typeV
to an attributeA
on an elementE
. It will also provide the implementation for how to do this, since it depends.Similarly,
Emits[F, E, Ev]
is evidence that elementE
emits events of typeEv
.This enables some interesting things:
A
can be used only with the specific elementE
that actually support them.A
may have a different implementation or even a different type depending on the elementE
it is modifying.If this works and type inference is ergonomic, then this should be a completely source-compatible change, hidden from users.
The text was updated successfully, but these errors were encountered: