
@samth it I’d soooo small! Making me sad.

@zenspider ?

@samth just being wistful… This conference used to be huge. I worry it won’t have the impact it used to have.

@chrisg has joined the channel

So I’m using Racket for a class and discovered this, if I do (require 2htdp/planetcute) and then (list heart heart), the REPL only shows one image (the heart isn’t repeated), though interestingly if I ask for the length of that same list, it’s 2

Any idea if that’s expected behavior? Easy enough to tell my class to ignore the fact that the repl isn’t printing out the expected list so it’s not a big deal, just thought I’d bring it up

@chrisg that’s definitely a bug

Thanks @samth, I’ll report it as such :thumbsup:

@samth I just did Chris Granger’s “how long does it take to make a counting button” example… Even learning enough of racket/gui
it only took me 3–4 minutes… but is there something I can have represented in the repl that could do the same thing? I had to have a [parent frame]
thingy to make this demo work and would just like to have a thing drop into the repl w/ some state.

@zenspider I don’t think there’s a really easy way to add a button to the repl, but I know very little racket/gui
programming

looks like button-snip%
would be close if it weren’t backed by an image

oh! text-button-snip%

hrm. nope :confused:

@jason_gregory has joined the channel

I’m trying to wrap my head around units and how to think about them. They seem mostly prevalent in gui code written in the pre-Racket days (PLT Games, Slideshow, etc), but they seem like a pretty powerful construct for building other applications and structuring libraries around.

As an example, @lexi.lambda’s envy project seems like a natural fit. envy could produce a signature, and the environment parsing could produce a unit that matches the signature for use everywhere.

but, in testing, you could provide a “mocked” unit…

but, based on my peering into slideshow’s code, it seems like units “infect” — I’m not sure if that’s just the style of slideshow’s codebase, or a practical “limitation” of working with them. Is my understanding accurate?

And, finally: does it make sense to think of a unit as a “parameterizable, reinvokable” module?

@apg Yes, a parameterizable, reinvokable module is definitely the right idea. A unit cannot import or export macros, although macros can be “exported” in unit signatures. Yes, units tend infect if you want to parameterize in interesting ways, and the overhead of doing that is why they’re not used as pervasively as we originally imagined. Units are still a decent option for dealing with mutual dependencies, since units allow cycles in the dependency graph, and modules don’t. Units can also be good for parameterization, but lambda
turns out to be enough more often than not.

@mflatt thanks for the explanation! the other analogy I thought of is simply a class. but, that’s more squinty.

what if units were extended to support a notion of calling into — send
in class terms, such that they could be linked without affecting local bindings (which is where I think the “infectiousness” stems from)?

so for every exportable you could write the equivalent of (unit@ some-function-in-sig)
?

to call the some-function-in-sig
without binding it in your current module’s namespace

sort of a dynamic “fluid” import of sorts

You can use (let () (define-values/invoke-unit ....) ....)
to do that kind of thing.

hmm.. does invoking the unit cost a lot though?

It costs some, similar to calling a function that contains definitions in its body. If you want a single instance that acts like an object, then you could certainly implement that macro. There’s a compile-time API to inspect signatures.

yeah, i guess the thing I want is temporary, or fluid “linkage”

if the unit performs some sort of expensive computation, invoking it multiple times to call a single function from it (like, with the define-values/invoke-unit) seems costly

where as, an already invoked unit that wasn’t linked (if such a thing is possible) provides this sort of first class object that I can “tap” into when needed only.

but, i guess i could just use a class for that, too.

does any of this make sense?

with the advantage that these things can be parameterized and customized effectively.

(e.g. a plugin system or something)

Yes, that all make sense, including the idea that you might just use a class (possibly under a lambda
)

is there an easy way in racket/class classes to enforce a thing implements an interface, in the same way a unit is checked against a signature?

I guess as a final point, maybe the best idea is a macro, like you suggested (and I’m just catching up to), that invokes a unit, captures and saves off it’s exports into a first class thing (e.g. a struct) that can get the dynamic linkage I’m searching for, without the cost of reinvoking the unit each time.

maybe a struct with a prop:procedure that ties it all together. :thinking_face:

thanks again, @mflatt

@apg If you want runtime linkage another idea is to start with dynamic-require
, and build up from that that?

Let’s say, the plugin interface requires plugins to provide functions named start
do
and finish
— or provide a struct or class named the-interface
.

On the consuming side your app could store the dynamic-require
d result(s) in var(s) wrapped in contract(s) if you wish.

(Maybe I didn’t follow the discussion well. Just wanted to point out that dynamic-require
is a thing. Maybe too low-level for what you’re trying to do.)

@greg I’m not, yet, trying to do anything. Just exploring what units are (and are not) good for.

dynamic-require
sounds worth exploring, though I’m not sure it gets at the ability to “mock” things like in my example with envy.

maybe.

hmmm, yeah, I guess other than the signature aspect of it, @greg, this could work the same way.

you still get the separation of namespaces that are important in this theoretical use case. worth considering, for sure.

does anyone have context on neil van dyke’s “uri-old” package?

is it called “uri-old” because there’s a new thing? or something else?

oh. nevermind. it’s “old and obsolete, but nevertheless still in use.”

I ask, mostly, because I’m curious why net/url doesn’t provide a #url"<http://blargh>"
reader like uri-old does.

@mflatt Do you have any intuition around whether or not it would be possible to make splicing-syntax-parameterize
used in a 'module-begin
context apply to declarations lifted with syntax-local-lift-module-begin-declaration
? It seems like it would be necessary to force expansion using local-expand
, but that seems like it would be okay in a 'module-begin
context, since the expander is going to recursively expand the module, anyway. Does that make sense, or am I speaking nonsense?

@lexi.lambda Do you mean local-expand/capture-lifts
? It sounds like that could work.

I just mean local-expand
… my understanding is local-expand/capture-lifts
is only for syntax-local-lift-expression
. I want to make this work for something that uses syntax-local-lift-module-end-declaration
.

Oh, I see. I’m less optimistic about that.

I’ve hacked something together since I asked the question, and it passes the tests I wrote. However, it doesn’t seem to be working in my “real” problem, so I’m not sure what’s going on.

I did observe that calling local-expand
with 'module-begin
as the context seems to, indeed, capture things lifted by syntax-local-lift-module-end-declaration
.

For context, here are the changes I attempted so far: https://github.com/lexi-lambda/racket/commit/eb5a3be3c8c4ca2e5b3e5c8eed06b60b78e13262

My guess is that expansion for 'module-begin
either goes too far or a partial doesn’t go far enough to expose all of the relevant lifts, but maybe it can be made to work

When in a 'module-begin
context, after the initial introduction of #%module-begin
, doesn’t the expander recursively expand the module, anyway? There’s no more partial expansion at that point, right?

Yes, but I was thinking that you need something to be partial to add the parameterization. But maybe I haven’t given it enough thought, and I can look more tomorrow

Okay. My naïve attempt just bound the identifiers in a first-class definition context, then called (local-expand stx 'module-begin '() ctx)
.

The main reason I’m trying this, for what it’s worth, is to avoid needing to reimplement module+
for Hackett. I started by trying to ditch syntax parameters entirely and just use phase 1 parameters, but I don’t think that interacts with submodules properly, an issue I stumbled into before.

So it seems like binding the right identifiers and forcing recursive expansion should work alright, even if it’s a little roundabout. But it isn’t working, so I’m trying to figure out why.

@mflatt Here’s a reduced program I’ve managed to come up with that completely perplexes me: #lang racket
(require syntax/parse/define)
(define-syntax x #f)
(define-simple-macro (print-shadower!)
#:do [(println (syntax-local-value (syntax-local-get-shadower #'x)))]
(void))
(define-simple-macro (expand-and-shadow form ...)
#:do [(define ctx (syntax-local-make-definition-context))
(syntax-local-bind-syntaxes (list (syntax-local-introduce (syntax-local-get-shadower #'x)))
#'#t
ctx)]
#:with result (local-expand #'(#%plain-module-begin form ...) 'module-begin '() ctx)
#:do [(println (syntax->datum #'result))]
result)
(module* a #f (expand-and-shadow (print-shadower!)))
(module* b #f (expand-and-shadow (define y (print-shadower!))))
Here’s the output: #t
'(#%plain-module-begin (#%app void))
#f
'(#%plain-module-begin (define-values (y) (#%app void)))
Why does the second example print #f
?

I think I must not understand how first-class definition contexts work.

Also, excitingly, it seems that using syntax-local-make-definition-context
in a 'module-begin
context produces an internal expander error on racket7.

Here’s an even simpler test case: #lang racket
(require syntax/parse/define)
(define-simple-macro (bind-and-expand x:id form ...)
#:do [(define ctx (syntax-local-make-definition-context))
(syntax-local-bind-syntaxes (list #'x) #'(λ (stx) (println stx) #'(void)) ctx)]
#:with result (local-expand #'(#%plain-module-begin form ...) 'module-begin '() ctx)
#:do [(writeln (syntax->datum #'result))]
result)
(module* a #f (bind-and-expand x x))
(module* b #f (bind-and-expand x (define y x)))
The first module*
declaration compiles fine, but the second produces an identifier-used-out-of-context error.

This weirdness really does seem potentially specific to 'module-begin
because I can’t get similar behavior with other expansion contexts, and the fact that it crashes the new expander seems like reason to believe it’s doing something strange.

I have Many Opinions about both this and uris in general