carl.morris.world
2019-3-12 10:45:55

@carl.morris.world has joined the channel


gknauth
2019-3-12 11:14:26

@oldsin, I had to laugh. The Scribble manual is not too long, true, but sometimes docs are Bible-length, and I was imagining someone asking a Bible-related question and then apologizing, “Sorry for not reading the full Bible before asking.” I’m sure your question helped someone who did not already know the answer. The Racket community is by far the friendliest and most helpful technology community I’ve encountered.


soegaard2
2019-3-12 12:20:39

These days the question isn’t “can I” but “how do I”.


a.nastaev
2019-3-12 12:33:40

A quick question here and thanks in advance for possible help!

Is there any way in DrRacket that FunctionName and InputName could be distinguished in different colors? Like in Elixir or any other languages.

Thank you!


soegaard2
2019-3-12 12:37:07

I don’t think so. In DrRacket find “Preferences…” in the “DrRacket” menu. Then click the tab “Colors”.


soegaard2
2019-3-12 12:37:27

You can now see what’s possible to change.


soegaard2
2019-3-12 12:38:01

Note also that colors change after clicking the “Check Syntax” icon (upper right corner: check mark+looking glass).


soegaard2
2019-3-12 12:38:21

Then you can hover over an identifier and see arrows that show the binding.


mflatt
2019-3-12 12:39:27

I don’t think so. A common strategy (which you’ve probably already considered) is to parameterize over the language with a macro.


a.nastaev
2019-3-12 13:06:00

Hah! This is actually cool @soegaard2 Thanks a lot!


abmclin
2019-3-12 16:03:08

I’m curious about why struct doesn’t support individually defined defaults for struct fields? As far as I can tell the only option is to use #:auto and the corresponding #:auto-value which unfortunately doesn’t quite do what I want to be able to do.

I’d like to be able to do something like

(struct a (b c [d #:default 'hi]) #:transparent) (define test1 (a 1 2)) -> (a 1 2 'hi) (define test2 (a 1 2 'hello)) -> (a 1 2 'hello)

Am I completely out of luck?


samth
2019-3-12 16:04:20

@abmclin you can use one of several packages that provide abstractions over struct


abmclin
2019-3-12 16:05:08

ok, my issue now is that I’m constrained to using just pre-base and available modules in racket/private I’ll think of something


samth
2019-3-12 16:05:53

in that case you’re out of luck — the underlying make-struct-type is constrained that way


samth
2019-3-12 16:06:02

I would just write a constructor that does what you want


abmclin
2019-3-12 16:06:16

yeah that’s what I’m thinking as well, thanks


notjack
2019-3-12 16:59:45

@samth why was the #:auto feature originally introduced? the feature seems kind of bizarre


soegaard2
2019-3-12 17:03:21

It’s an old feature. It’s present in PLT Scheme 372: http://download.plt-scheme.org/doc/372/pdf/mzscheme.pdf (page 30)


notjack
2019-3-12 17:04:16

how old is that?


soegaard2
2019-3-12 17:04:47

It’s not present in PLT Scheme 103 (from year 2000).


soegaard2
2019-3-12 17:05:35

soegaard2
2019-3-12 17:06:25

leif
2019-3-12 19:02:42

@mflatt & @robby, I think that @michael.ballantyne and I found a bug in either syntax-local-lift-expression or the contract library: https://gist.github.com/LeifAndersen/5edd52dd2a6e4b1d5a116790ddf1add2


leif
2019-3-12 19:02:43

leif
2019-3-12 19:03:07

(It doesn’t have to be pict, anything that uses contra t-out seems to have the same bug.


robby
2019-3-12 19:04:58

Not sure what’s going on, but this one doesn’t do it:


robby
2019-3-12 19:05:01

(module m racket/base
  (require racket/contract)
  (provide (contract-out [x any/c]))
  (define x 1))

(require (submod "." m))
(define-syntax (m stx)
  #`(list #,(syntax-local-lift-expression #'x)))
(#%expression (m))

leif
2019-3-12 19:07:05

The x needs to be in both the lift and directly in the macro. So this one does do it:


leif
2019-3-12 19:07:16
#lang racket

(module m racket/base
  (require racket/contract)
  (provide (contract-out [x any/c]))
  (define x 1))

(require (submod "." m))
(define-syntax (m stx)
  #`(list x #,(syntax-local-lift-expression #'x)))
(#%expression (m))

leif
2019-3-12 19:07:44

(The difference being the second to last line: #`(list x #,(syntax-local-lift-expression #'x))))


robby
2019-3-12 19:08:00

I see.


robby
2019-3-12 19:08:58

might be a bug in this function:



michael.ballantyne
2019-3-12 19:09:47

The direct output gets expanded first and lifts something to do variable-reference->module-source; the lifted output is expanded later and uses a cached reference to that same lift, but to a definition that ends up appearing lower in the module.


robby
2019-3-12 19:10:21

that does indeed sound like a bug in the linked function.


robby
2019-3-12 19:10:30

maybe


robby
2019-3-12 19:11:35

in any case, the code I linked to is what is managing the cache you mention


leif
2019-3-12 19:12:15

@robby YUp, and looks like it converts the identifiers in the source to the lifted varients, yes?


leif
2019-3-12 19:12:18

variants*


robby
2019-3-12 19:14:32

Oh, I think I get it. Maybe. But I don’t see how to fix it.


michael.ballantyne
2019-3-12 19:15:28

I’m not sure how to fix it without either 1. giving up on caching or 2. changing the rules for lifts. The basic assumption that lifts introduced by expressions that expand earlier will execute earlier is wrong.


michael.ballantyne
2019-3-12 19:16:12

I could imagine the expansion of lifts being different, such that the lifted expression is expanded immediately instead of delayed, but I don’t know how much other code that might break.


robby
2019-3-12 19:16:30

that sounds like a scary change


robby
2019-3-12 19:16:54

I think it would be okay if the macro just got a little more information and then used two different entries in the cache for these two situations (lifted vs not) somehow


robby
2019-3-12 19:17:45

The goal of this lifting is to avoid creating wrappers multiple times for some given export (say circle) that is used many times in a single module.


robby
2019-3-12 19:18:33

So having some number of wrappers proportional to the number of times lifts are invoked inside lifts would be fine, I presume


leif
2019-3-12 19:18:42

Honestly, it seems to me like caching ‘should’ be fine. Is there any reason why syntax-local-lift-expression can’t do a binding dependency walk to see what order things should go in?


robby
2019-3-12 19:19:00

that’d be easier for me. :wink:


leif
2019-3-12 19:19:04

lol


robby
2019-3-12 19:19:24

But it doesn’t sound like the kind of thing we might expect the macro system to do.


robby
2019-3-12 19:19:29

Not that I’m an expert here


leif
2019-3-12 19:19:46

Although honestly @robby I would much prefer to have a cache lift. Making a new lift whenever an identifier that uses a contract is used is….well seems inefficient.


leif
2019-3-12 19:20:10

Fair. I would probably defer to @michael.ballantyne or @mflatt here, as they know way more than me. :slightly_smiling_face:


robby
2019-3-12 19:21:35

I don’t think that this particular inefficiency is going to matter in practice. But if you see somewhere where people are lifting inside lifts that lift inside lifts etc etc etc and use contracts and those contracts are creating too many wrappers, then lets figure out a better way. (Or if it isn’t possible to communicate something that could be used as suitable key.)


michael.ballantyne
2019-3-12 19:26:00

Looks like the expansion of lifts is delayed so that local-expand/capture-lifts can exist.


leif
2019-3-12 19:28:02

Err…I have been known to write macros that are hairy enough to do that. :confused:


leif
2019-3-12 19:28:20

(I mean, I don’t want to write lifts in lifts….But sometimes there isn’t any other way)


michael.ballantyne
2019-3-12 19:29:32

And I don’t see a way for the contract macros to figure out whether they’re inside a syntax-local-lift-expression or not.


michael.ballantyne
2019-3-12 19:29:39

The syntax-local-context is ’expression.


michael.ballantyne
2019-3-12 19:29:54

The syntax-local-lift-context isn’t changed.


robby
2019-3-12 19:30:27

Add syntax-local-context/including-number-of-lifts that is like syntax-local-context but might return something with a natural inside?


robby
2019-3-12 19:30:47

(well, the name is a bit on the short side for my taste, so maybe that’s not a good idea :wink:)


leif
2019-3-12 19:31:00

lol….also eww…..but mostly lol.


leif
2019-3-12 19:31:14

You’re right, the problem is that the name needs to be roughly 10x longer.


leif
2019-3-12 19:31:35

That is certainly an interesting idea though.


ryanc
2019-3-12 20:00:17

What if syntax-local-lift-context returned an element in a partial order instead of an equivalence class, where ctx1 <= ctx2 meant that all of the lifts of ctx1 are available in ctx2?


michael.ballantyne
2019-3-12 20:00:19

The only use of local-expand/capture-lifts in the standard distribution collections appears to be in Typed Racket. It could be adapted to work with a modification of the expander that expands as it lifts, instead of delaying. I don’t have an easy way of searching the rest of the universe of Racket code…


michael.ballantyne
2019-3-12 20:00:43

@ryanc do you see a good reason expansion of lifted expressions is delayed?


robby
2019-3-12 20:02:39

@ryanc depending what you answer to MB: the partial order thing isn’t as amenable to the cache.


ryanc
2019-3-12 20:03:31

@michael.ballantyne If it’s not delayed, then the context needs to be fixed up before expansion. Consider syntax-parameters, for example, and maybe other things defined in Racket that the macro expander doesn’t know about. I don’t know how much of an issue that is, but that’s what I would worry about.


michael.ballantyne
2019-3-12 20:03:59

What do you mean about the context needing to be fixed up? Scopes? Environment?


michael.ballantyne
2019-3-12 20:04:23

Ah. Environment, like syntax parameters.


michael.ballantyne
2019-3-12 20:04:31

I see.


michael.ballantyne
2019-3-12 20:05:04

It’s an algebraic effects problem! The effect of lifted expansion needs to happen outside the scope of local effects like syntax parameterize.


michael.ballantyne
2019-3-12 20:07:46

If only the expander didn’t install a continuation barrier. :slightly_smiling_face:


ryanc
2019-3-12 20:08:16

@michael.ballantyne sorry, my fault :slightly_smiling_face:


michael.ballantyne
2019-3-12 20:08:54

(I’ve run into other places where I really want to save off things like the current introduction scope in a continuation and restore it later)


lexi.lambda
2019-3-12 20:12:02

(You could stash an introduction scope somewhere easily enough, but obviously the other things are much harder—like the binding context.)


michael.ballantyne
2019-3-12 20:12:49

The problem is I can’t restore the introduction scope.


lexi.lambda
2019-3-12 20:12:57

Ah, right.


lexi.lambda
2019-3-12 20:13:32

What are the use cases for local-expand/capture-lifts? Aside from basically “Typed Racket”.


notjack
2019-3-12 20:14:07

wondering that myself


ryanc
2019-3-12 20:14:15

@robby It seems like this would work with caching, it would just move some of the responsibility to the macro expander. Instead of a target meaning both “this is where lifts go” and “this is what’s available” (which this bug demonstrates is not true), it would only mean the latter. So the cache would be a Key =&gt; (listof (pair Ctx Id)) and before lifting you would have to check if the current context extended any of the existing ones. Would that work?


michael.ballantyne
2019-3-12 20:14:16

Typed Racket uses it to typecheck the lifted expression, I think. Other contexts where you’re re-writing fully expanded Racket might do something similar.


robby
2019-3-12 20:14:52

I can do that


samth
2019-3-12 20:14:54

I think the use case in TR is more specific/general than that — just wanting to expand something and put all the relevant pieces in one place


lexi.lambda
2019-3-12 20:15:20

It seems like you could imagine situations where capturing lifts in a more local context could be theoretically useful, but I imagine a lot of macros that use syntax-local-lift-expression assume the result is going to end up at a module top level, and I don’t know if that would be an easy invariant to maintain unless you’re doing a TR-like thing in #%module-begin.


notjack
2019-3-12 20:15:33

in general, would “local-expand from a module context that wants to do whole-module transformations” be a good place to use it?


samth
2019-3-12 20:15:54

that isn’t really where Typed Racket uses it


notjack
2019-3-12 20:16:08

oh, where does TR use it?


samth
2019-3-12 20:18:05

it’s used in require/contract (part of the implementation of require/typed but needed to implement require/contract as an abstraction), in how the toplevel works, and in with-types


leif
2019-3-12 20:49:33

@ryanc I don’t see how changing syntax-local-lift-context would help with caching in syntax-local-lift-expression?


ryanc
2019-3-12 20:59:24

@leif The idea is to change syntax-local-lift-context to return something that represents whether a previously lifted identifier is actually available here and now. Thinking about it some more, my idea of a partial order isn’t quite right, or at least it would be a little awkward—you’d have to get the context, check it, lift, and then get the new context and save that in the cache. Perhaps it would be better to have a predicate that takes a lift context and an identifier and returns true if the identifier is available (previously lifted to that context or a parent and defined above the current expansion point).


ryanc
2019-3-12 21:05:26

In other words, the contract library currently assumes that if an expression was previously sent to the current lift context, the resulting id is available. Your example shows that the order of sends doesn’t always correspond to the order of receives (that is, the order that lifted definitions occur in the expanded module). So hopefully a fix would be to check whether the lifted expression was previously received (that is, its definition occurs above the current expansion position).


leif
2019-3-12 21:06:50

Ah, okay, that makes sense.


ryanc
2019-3-12 21:11:52

Thanks for the question; writing the answer helped clarify my thoughts. I (currently?) think the new predicate on identifiers would be the easiest solution.


ryanc
2019-3-12 21:15:01

It’s a little like a degenerate version of the letrec/undefined analysis exposed as part of the macro API.


leif
2019-3-12 21:16:37

Mmmm…so it seems like it would technically break backwards compatibility. But not in any meaningful way, is that correct?


ryanc
2019-3-12 21:24:51

Sorry, forget what I said about changing syntax-local-lift-context. Let’s say we add a new thing, like syntax-local-defined-above? : Identifier -&gt; Boolean. The contract cache would maintain a mapping Key =&gt; (Listof Identifier). Each time you lift the expression corresponding to a key, you add the resulting identifier to the key’s associated list. But before doing a new lift, you check the key and see if any of the identifiers are syntax-local-defined-above?; if so, it’s safe to just use that identifier as a reference.


robby
2019-3-12 22:17:25

Sounds straightforward!


florence
2019-3-13 00:37:45

pop-pl uses it because to expands into a unit and some of its lift’s bind state that should be local to each unit instantiation. (Not that I ever claim that pop-pl does anything the right way)