First class compound datatypes that can Racket

47 views
Skip to first unread message

zeRusski

unread,
May 22, 2019, 11:40:21 AM5/22/19
to Racket Users
This would probably sound like rambling but that's only because I am struggling a
little bit. I implemented a little language that offers its own compound data
type: first class and users can extend it in various ways. Naturally, it is
implemented as a Racket struct. As I started using the language, it occurred to
me that I lost something and I'd very much like to get it back.

Racket struct offers some truly powerful machinery that permeates Racket
ecosystem. Here's a motivating example: having a new fancy first class compound
datatype (tm) is nice and well but what if I want it to double as a
synchronizable event? Oops. I do facilitate extensions, but that's something that
would need prop:evt on the underlying struct. I could "extend" my language and
add this prop myself, but it isn't a given that every instance needs to be an
event, not to mention there isn't "one size fits all" here, and the user may want
to customize the result of synchronization, if they even want events at all. More
generally though, how about other properties that may not even exist yet? Of
course I could surgically extend my implementation and allow to customize those
extensions etc. But that kind of opens pandora's box, not to mention most of the
time it'll simply be a "passthrough" of what Racket structs can already do, and
all of this nonsense would have to be documented - again why bother given the
marvel that is Racket documentation?

Conventional wisdom holds that you don't expose implementation details, but
honestly I'm ok dispensing with the dogma in this case. It isn't obvious to me how
to do that, though. Suppose, you derive a new stuct somehow: say, it implements
prop:evt but must otherwise be like your datatype. What does that mean? Struct
inheritance isn't that - I know that much. It must be a protocol of some kind - a
set of functions and what not (behaviors, really) that make your fancy datatype
what it is. One possible solution is Racket generics that is assuming we can
capture the essence of our type as a set of methods. Suppose for a moment, that we
could. While the underlying implementation may have changed and become either
richer or more constrained, it should still act as our fancy datatype. Since
Racket generics don't delegate to base types, are we to demand that the user
extends the interface to the struct that is nothing but a wrapper around another
struct that already implements said interface? That's asking too much IMO.

Is the answer to offer a macro that expands into something like

  (struct extended-type fancy-type () #:methods gen:fancy-iface ...) 

where I suspect fancy-iface methods don't need to change at all between macro
invocations?

This can't be a new problem. Any thoughts or advice?

Greg Hendershott

unread,
May 22, 2019, 12:11:27 PM5/22/19
to zeRusski, Racket Users
Just spitballing here, trying to start with "what's the simplest
possible thing that could work?":

You could let the user flag a field as the event. I don't know your
surface syntax, but maybe using an `#:as-evt` keyword would be OK?

You could link/refer the user to the `prop:evt` docs, or, copypasta the
relevant bullet points. (~= "the field must be an event?, or, a
procedure that takes an instance of the struct, yada yada...").

`never-evt` is a reasonable default value if the user hasn't generated
any. Unless you think `always-evt` would be. :)

IIUC this is a struct _type_ property, so it won't waste space
per-struct to specify `#:prop:evt never-evt` as the default. I think.


p.s. While you "have the hood open", you might also want to do something
similar for `prop:procedure`?

zeRusski

unread,
May 22, 2019, 3:04:15 PM5/22/19
to Racket Users
p.s. While you "have the hood open", you might also want to do something
similar for `prop:procedure`?

I would agree that it is A solution to this particular problem with this
particular prop. The "passthrough" of some form or other works well and is always
open to me as the language maintainer but it amounts to special-casing things and
making me the sole arbiter of what makes it into the language and what doesn't.
Notice however that nothing about our fancy datatype changes, its interface
remains the same, yet user gets a richer type. Which means there ought to be a way
to generalize this. To use your analogy I'd like to find out if there's a way to
"leave the hood open" just enough or at least let the user do the "passthrough"
trick without the need to dismantle the entire car.

Greg Hendershott

unread,
May 22, 2019, 10:18:32 PM5/22/19
to Racket Users
Apparently I was too specific and pragmatic.

Let me try the opposite: Maybe more general than you want. :)

After spending some years with Racket, I've noticed certain things are
very popular targets of extension or customization. One is `define`.
Another is `struct`.

The catch is, these various customizations don't necessarily
combine/compose well, if at all.

I haven't thought about this very much. My first question would be,
could there be something roughly like define-match-expander -- a
"define-struct-expander" and a (ouch) "define-define-expander" -- that
would help? I don't even know if is a very difficult problem, or, very
easy and someone has already figured it out and it just needs more
"promotion" and uptake.

For example, I believe David Storrs has been working a lot recently on
struct-plus-plus. What if someone wanted to use some aspects of that and
also of what you're doing, that weren't inherently incompatible. And of
course also, as you mentioned, use aspects of plain old `struct`. Is
that even possible? How would that even work?
Reply all
Reply to author
Forward
0 new messages