
When I provide
with contract-out
a struct definition, where the struct has a super struct, there does not seem to exist the accessor for the fields that come from the super struct. That can’t be right, surely? Am I just missing something? (Example to follow)

#lang racket/base
(require racket/contract)
(module *mod* racket/base
(require racket/contract)
(provide
(contract-out
(struct sup
([fld1 number?]))
(struct sub
([fld1 number?]
[fld2 number?]))))
(struct sup
(fld1) #:transparent)
(struct sub sup
(fld2) #:transparent))
(require '*mod*)
(define S (sub 1 2))

After this, however. > S
(sub 1 2)
> (sub-fld2 S)
2
> (sub-fld1 S)
; sub-fld1: undefined;
; cannot reference an identifier before its definition
; in module: ",,,test.rkt"

You need the struct name of the parent: (sup-fld1 S)

Oh!

Is that how that’s always worked?

Well, anyway, thanks so much. That certainly answers my question!

yes :slightly_smiling_face:

:point_up_2:this would be a great ‘today I learnt’ to share with others via the Racket Discourse: https://racket.discourse.group/tag/today-i-learnt

Reminder the Racket meet-up is Saturday 18:00 UTC
I can’t attend as I will be at my local music festival so have fun

Done, I think!

Awesome! Thank you!

May the Fourth be with you all today. And in the spirit of such a grand day, I shall leave you with a nice little joke:
Q: What is the internal temperature of a Tauntaun? A: Lukewarm

Do I get this right: lambda
s not attached to a define
tend to prevent a number of optimizations, e.g. inlining?

By definition a closure (with lexical context) can’t be inlined because it’s created at runtime.

So, that optimization is out the window. There are probably others as well: folding for knowably immutable+constant values, probably various detections of short-lived objects for GC would be another.

Also, lambda with keyword argument not attached to define
can’t be inlined.

> For direct calls to functions with keyword arguments, the compiler can typically check keyword arguments statically and generate a direct call to a non-keyword variant of the function, which reduces the run-time overhead of keyword checking. This optimization applies only for keyword-accepting procedures that are bound with define.


@laurent.orseau and @massung: “tend to”, but not always. For example, if you start Racket CS with PLT_LINKLET_SHOW_CP0=1 racket
and evaluate this code: (lambda (xs) (and (list? xs) (map (lambda (n) (+ n 1)) xs)))
, you’ll see that after “cp0” (which, IIRC, does inlining and other high-level optimizations), both map
and the inner lambda
have been inlined away. In the cp0 version, I think only the outer lambda
s will result in closure allocations; I think the letrec
-bound lambda
will just turn into a label for direct calls.

In that example it’s not a closure with lexical context though. It’s just a function that takes a single argument that can be globally defined and called.

Btw, https://docs.racket-lang.org/loop/index.html#%28part._.Performance%29 is a concrete example where the inlining fails.

Try (lambda (xs c) (and (list? xs) (map (lambda (n) (+ n c)) xs)))
or (lambda (xs) (and (list? xs) (map (let ([c 1]) (lambda (n) (+ n c))) xs)))
. In both cases, the inner lambda
has free variables (what you’re calling a “closure”, I think), but it still gets inlined away. (A closure is one implementation strategy for lambda
, but the compiler decides whether a lambda expression needs to be represented as a closure. So it doesn’t make sense to say that the compiler can’t inline closures. You could claim that the compiler cannot inline lambda
expressions with free variables, but that is wrong; see the examples above.)

Quite informative, thanks!

@ryanc - makes sense. Perhaps more qualifying is that it can’t inline lambda expressions with free variables outside the scope they are declared in?

For example, there’s no world I can envision in which map
could inline a function passed to it. It might be possible to inline map
itself with an inlined version of the supplied function, though.