Re: [Haskell-cafe] Generic "annotations" on type definitions

Hello all,
I was recently thinking about a generic programming application (auto-generating UI code from data definitions) that would benefit from the ability to "annotate" data definitions. For example, let's say you have a type
data Foo = Foo {barred :: Boolean}
You are (auto-)generating a UI to display and manipulate `Foo`s. Perhaps the generic UI code defaults to making a check-box for Boolean values, but you would rather have it be a radio button. It would be advantageous to be able to write something like e.g.
data Foo = Foo {barred :: Boolean # RenderMode RadioButton}
In many languages, you have the ability to annotate data fields in a way which has no bearing whatsoever on the first-order behavior or representation of the type, and is only reflected via the language's generic programming API. For example, in Java, you have
class Foo { @RenderMode(RadioButton) bool barred; }
And in Rust, you have struct Foo { #[render_mode(radio_button)] barred : bool }
In Haskell, I do not believe we have any such system in place for interfacing with Generics.
The closest I am familiar with is from the optparse-generic package, where you write code of the form
data Foo w = Foo {barred :: w ::: Boolean > RenderMode RadioButton}
See https://hackage.haskell.org/package/optparse-generic-1.4.8/docs/Options-Gene...:: ::
This is basically a clever way to sort of re-create what we have available in Rust or Java, using type families, but with the annoying downside that it makes interacting with your type in normal code much more cumbersome, and is also relatively confusing to read and write.
Compare Rust's Clap package, which is very similar to optparse-generic, but more convenient to work with because the annotations don't impact normal usage of the type.
See https://docs.rs/clap/latest/clap/#example
Would it be technically feasible to add such a thing to GHC's Generics support? I could imagine something like updating the GHC.Generics Meta type's MetaCons constructor to
MetaCons Symbol FixityI Bool *[Annotation]*
Best, Will
Why not use special syntax in comments, like we do for Haddock, doctests etc. data Foo = Foo { barred :: Boolean -- ^ #GUI[RenderMode RadioButton] } There is a functional language capable of auto-generating input forms: Clean https://clean.cs.ru.nl/Clean with the iTask library on top. It can also generate forms for recursive types. The Dutch coast guard uses it to interactively design queries to a database of marine vessel movements. It'd be cool if Haskell's Generics also could do that. Olaf

Doesn't this require TH? A primary constraint here is using generics only, since TH has many undesirable properties. In the original question, I gave an unfair advantage to Rust, because it uses macros, which are similar to TH. Java's system is perhaps a fairer comparison, although it works mostly via runtime inspection. The idea is to provide information about a type to Generic, without changing the way that type behaves for programmers. Will
On Sep 10, 2022, at 13:26, Olaf Klinke
wrote:
Hello all,
I was recently thinking about a generic programming application (auto-generating UI code from data definitions) that would benefit from the ability to "annotate" data definitions. For example, let's say you have a type
data Foo = Foo {barred :: Boolean}
You are (auto-)generating a UI to display and manipulate `Foo`s. Perhaps the generic UI code defaults to making a check-box for Boolean values, but you would rather have it be a radio button. It would be advantageous to be able to write something like e.g.
data Foo = Foo {barred :: Boolean # RenderMode RadioButton}
In many languages, you have the ability to annotate data fields in a way which has no bearing whatsoever on the first-order behavior or representation of the type, and is only reflected via the language's generic programming API. For example, in Java, you have
class Foo { @RenderMode(RadioButton) bool barred; }
And in Rust, you have struct Foo { #[render_mode(radio_button)] barred : bool }
In Haskell, I do not believe we have any such system in place for interfacing with Generics.
The closest I am familiar with is from the optparse-generic package, where you write code of the form
data Foo w = Foo {barred :: w ::: Boolean > RenderMode RadioButton}
See https://hackage.haskell.org/package/optparse-generic-1.4.8/docs/Options-Gene...:: ::
This is basically a clever way to sort of re-create what we have available in Rust or Java, using type families, but with the annoying downside that it makes interacting with your type in normal code much more cumbersome, and is also relatively confusing to read and write.
Compare Rust's Clap package, which is very similar to optparse-generic, but more convenient to work with because the annotations don't impact normal usage of the type.
See https://docs.rs/clap/latest/clap/#example
Would it be technically feasible to add such a thing to GHC's Generics support? I could imagine something like updating the GHC.Generics Meta type's MetaCons constructor to
MetaCons Symbol FixityI Bool *[Annotation]*
Best, Will
Why not use special syntax in comments, like we do for Haddock, doctests etc.
data Foo = Foo { barred :: Boolean -- ^ #GUI[RenderMode RadioButton] }
There is a functional language capable of auto-generating input forms: Clean https://clean.cs.ru.nl/Clean with the iTask library on top. It can also generate forms for recursive types. The Dutch coast guard uses it to interactively design queries to a database of marine vessel movements. It'd be cool if Haskell's Generics also could do that.
Olaf

On Sat, 2022-09-10 at 15:20 -0400, Will Yager wrote:
Doesn't this require TH? A primary constraint here is using generics only, since TH has many undesirable properties.
In the original question, I gave an unfair advantage to Rust, because it uses macros, which are similar to TH. Java's system is perhaps a fairer comparison, although it works mostly via runtime inspection.
The idea is to provide information about a type to Generic, without changing the way that type behaves for programmers.
Will
Since your question is about choices of display/input, how about delaying the choice to the call site? To use your example, the UI generator knows that a Bool can be used with either check box or radio button, and the programmer must provide the choice via a settings record at every instance. This would potentially reduce clutter, as for many types there might only be one sensible UI choice available, hence a Data.Default.def suffices. The (admittedly hard) problem then is that the shape of the settings record must match the shape of the displayed type. In Haskell pseudocode: instance UI Bool where type UISettings Bool = RadioButton | CheckBox defaultUISettings = CheckBox instance (UI a, UI b) => UI (a :+: b) where type UISettings (a :+: b) = (UISettings a :*: UISettings b) -- must provide settings for both options inputForm :: UI a => UISettings a -> Form a I have personal interest in getting to work what you propose, so let's collaborate. Maybe Yesod's Form Monad is a good starting point for a proof-of-concept? Olaf
On Sep 10, 2022, at 13:26, Olaf Klinke
wrote:
Hello all,
I was recently thinking about a generic programming application (auto-generating UI code from data definitions) that would benefit from the ability to "annotate" data definitions. For example, let's say you have a type
data Foo = Foo {barred :: Boolean}
You are (auto-)generating a UI to display and manipulate `Foo`s. Perhaps the generic UI code defaults to making a check-box for Boolean values, but you would rather have it be a radio button. It would be advantageous to be able to write something like e.g.
data Foo = Foo {barred :: Boolean # RenderMode RadioButton}
In many languages, you have the ability to annotate data fields in a way which has no bearing whatsoever on the first-order behavior or representation of the type, and is only reflected via the language's generic programming API. For example, in Java, you have
class Foo { @RenderMode(RadioButton) bool barred; }
And in Rust, you have struct Foo { #[render_mode(radio_button)] barred : bool }
In Haskell, I do not believe we have any such system in place for interfacing with Generics.
The closest I am familiar with is from the optparse-generic package, where you write code of the form
data Foo w = Foo {barred :: w ::: Boolean > RenderMode RadioButton}
See https://hackage.haskell.org/package/optparse-generic-1.4.8/docs/Options-Gene...:: ::
This is basically a clever way to sort of re-create what we have available in Rust or Java, using type families, but with the annoying downside that it makes interacting with your type in normal code much more cumbersome, and is also relatively confusing to read and write.
Compare Rust's Clap package, which is very similar to optparse-generic, but more convenient to work with because the annotations don't impact normal usage of the type.
See https://docs.rs/clap/latest/clap/#example
Would it be technically feasible to add such a thing to GHC's Generics support? I could imagine something like updating the GHC.Generics Meta type's MetaCons constructor to
MetaCons Symbol FixityI Bool *[Annotation]*
Best, Will
Why not use special syntax in comments, like we do for Haddock, doctests etc.
data Foo = Foo { barred :: Boolean -- ^ #GUI[RenderMode RadioButton] }
There is a functional language capable of auto-generating input forms: Clean https://clean.cs.ru.nl/Clean with the iTask library on top. It can also generate forms for recursive types. The Dutch coast guard uses it to interactively design queries to a database of marine vessel movements. It'd be cool if Haskell's Generics also could do that.
Olaf

Doesn't this require TH? A primary constraint here is using generics only, since TH has many undesirable properties.
What syntax the metadata comes in is orthogonal to whether it's provided and processed using TH. That goes back to this previous question of yours:
Would it be technically feasible to add such a thing to GHC's Generics support?
Yes, it is technically feasible to extend GHC generics to reflect as much metadata as you want. It's a matter of deciding what syntax it's going to come in (whether in types, comments, or whatever else), how it's going to be encoded (via an extra `Meta` field, constructor, or whatever), and correspondingly adapting the code that generates Generic instances. However, changing GHC is something that takes a lot of effort. A short-term compromise is to reimplement the deriving of Generic instances in TH. TH would only be used for that, then the processing of that metadata by applications can be done purely in Haskell. Li-yao
participants (3)
-
Li-yao Xia
-
Olaf Klinke
-
Will Yager