chansey97
2022-3-19 13:30:01

Understanding prefab. Racket guide says that > Every https://docs.racket-lang.org/guide/define-struct.html#%28tech._prefab%29\|prefab structure type is https://docs.racket-lang.org/guide/define-struct.html#%28tech._transparent%29\|transparent—but even less abstract than a https://docs.racket-lang.org/guide/define-struct.html#%28tech._transparent%29\|transparent type, because instances can be created without any access to a particular structure-type declaration or existing examples. I don’t quite understand. Does it mean that I can create a prefab instance without structure-type declaration?

For example: #lang racket (struct sprout (kind) #:prefab) (sprout-kind #s(sprout garlic)) ;; => 'garlic It is OK.

However, #lang racket (sprout-kind #s(sprout garlic)) ; <---- does it create a instance even if no corresponding struct? ; sprout-kind: undefined; ; cannot reference an identifier before its definition ; in module: "e:\work-pl\racket\code\struct\prefab.rkt" Why I must (struct sprout (kind) #:prefab) before using #s(sprout garlic) ? Isn’t (struct sprout (kind) #:prefab) the structure-type declaration mentioned in guide?


ben.knoble
2022-3-19 13:33:54

The issue is sprout-kind, not the prefab syntax. You can effectively create instances of prefabs without having any struct type declarations, but you can’t do much with them in regards to accessing their fields.


ben.knoble
2022-3-19 13:34:32

I think you can serialize them and such, but to really manipulate them you want to get the accessor functions that struct defines.


chansey97
2022-3-19 13:35:52

Thanks. So Racket won’t auto create struct type declarations, it is just a instance printable serialized form.


laurent.orseau
2022-3-19 13:41:54

Elements of a prefab struct can still be accessed by index, by converting to a vector: > #s(sprout garlic) #s(sprout garlic) > (struct->vector #s(sprout garlic)) #(struct:sprout garlic) > (vector-ref (struct->vector #s(sprout garlic)) 1) garlic


chansey97
2022-3-19 14:08:18

Can I define two different auto-fields in struct? (struct posn (x y [z #:auto] [w #:auto]) #:transparent #:auto-value ???) ; <---- Guide says that > Specifies a value to be used for all automatic fields in the structure type So no way to define two different auto-fields?


soegaard2
2022-3-19 14:14:57

@chansey97 Normal struct declarations are generative. So if you evaluate (struct foo (bar baz)) twice, you’ll get two sets of structs (both named foo). If you make a (foo 1 2) with the constructor of the first one, foo-bar and friends from the second will refuse to work (since they are given structs of an incorrect type).

If you ever see an error such “foo-bar: expected a foo structure, given: (foo 1 2)” you’ll know what to look for.

In contrast prefab structures aren’t generative. You have have a single constructor.


chansey97
2022-3-19 14:33:45

Thanks. Is struct first-class value? (struct posn (x y) #:transparent) (define (raven-constructor super-type) (struct raven (name) #:super super-type #:transparent) raven) (let ((s (raven-constructor struct:posn))) (let ((instance (s 1 2 "hello"))) (posn-x instance) ; <---- ok (raven-name instance) ; <--- raven-name: undefined )) The return value of struct expression is a constructor procedure (not the struct itself), but how can I get name field of instance.

I found that we have a global identifier struct:posn, but no struct:raven in this example.


chansey97
2022-3-19 14:36:51

IIRC, the raven-constructor here is a bit like mixin mechanism in racket/class.


soegaard2
2022-3-19 14:45:47

#lang racket (struct posn (x y) #:transparent) (define (raven-constructor super-type) (struct raven (name) #:super super-type #:transparent) (values raven struct:raven)) (let-values ([(s st) (raven-constructor struct:posn)]) (define-values (name init-field-count auto-field-count acc-proc mut-proc imm-k-list super-type skipped?) (struct-type-info st)) (define (raven-name r) (acc-proc r 0)) (displayln (list name init-field-count auto-field-count acc-proc mut-proc mut-proc imm-k-list super-type skipped?)) (let ((instance (s 1 2 "hello"))) (posn-x instance) ; <---- ok (raven-name instance) ; <--- raven-name: undefined ))


soegaard2
2022-3-19 14:48:08

Alternatively, let raven-construtor return the functions you need: (struct posn (x y) #:transparent) (define (raven-constructor super-type) (struct raven (name) #:super super-type #:transparent) (values raven raven-name)) (let-values ([(s raven-name) (raven-constructor struct:posn)]) (let ((instance (s 1 2 "hello"))) (posn-x instance) ; <---- ok (raven-name instance) ; <--- raven-name: undefined ))


soegaard2
2022-3-19 14:48:35

But it depends on the situation.


chansey97
2022-3-19 14:52:56

@soegaard2 Very helpful, thanks!


samdphillips
2022-3-19 14:56:01

chansey97
2022-3-19 15:01:02

BTW, what is the struct: in struct:raven? Does it mean that Racket has a namespace struct and since #<struct-type:raven> is in that namespace, so we must prefix struct:?


soegaard2
2022-3-19 15:02:27

It’s not a namespace. The name struct:raven is just a standard identifier.


chansey97
2022-3-19 17:03:08

An interesting fact: struct:id is a structure type descriptor, which is a variable binding in runtime, the value of that binding is a first-class value. gen:id is a transformer binding, which can not be referred in runtime. I guess it only exists in macro expand time.


laurent.orseau
2022-3-19 17:40:00

Mini-quizz: There’s a simpler definition of pair->list than (define (pair->list p) (list (car p) (cdr p))) using primitives, for pairs of non-pairs. Will you find it? :slightly_smiling_face:


laurent.orseau
2022-3-19 17:42:23

(This is surely wellknown by long time schemers of course)


laurent.orseau
2022-3-19 17:44:12

(please reply in thread to avoid spoilers)


sorawee
2022-3-19 17:44:39

(define pair->list flatten) except that it doesn’t quite work if the car or cdr also contains pairs / lists.


laurent.orseau
2022-3-19 17:48:07

True


sorawee
2022-3-19 17:48:31

Wait, so is that the solution you have in mind?


laurent.orseau
2022-3-19 17:49:20

Sure. Anything else will require more characters than the original definition of course.


laurent.orseau
2022-3-19 17:49:53

(the caveat is important obviously)


laurent.orseau
2022-3-19 17:51:10

I often need to go from assocs of numbers to lists, in which case flatten is pretty handy


chansey97
2022-3-19 20:07:29

Is there a way to force Racket to print the content of a struct instance? For example, (struct cake (x y) #:transparent #:methods gen:custom-write [(define (write-proc self port mode) (fprintf port "This is my cake"))] ) (cake 1 2) ;; This is my cake But in some situation, I hope it print (cake 1 2) because it is easily to debug. Currently I have to modify struct cake .


sorawee
2022-3-19 20:12:29

If it’s transparent, perhaps try stuff like struct->list / struct->vector?


soegaard2
2022-3-19 20:13:31

I don’t get it. If you just have #:transparent and no gen:custom-write it should print the contents?


sorawee
2022-3-19 20:14:21

My read is that in the final product, @chansey97 wants the gen:custon-write behavior. But for debugging purpose, it’s nice to see the content.


soegaard2
2022-3-19 20:14:32

Ah.


chansey97
2022-3-19 20:16:02

I don’t want to modify source code of the project.


chansey97
2022-3-19 20:16:58

Or I have to use field-getter (but sometime’s you even hardly know the concrete struct type…). Of course, big project usually doesn’t use #:transparent, It is just a small example.


sschwarzer
2022-3-19 20:36:38

Speaking of #:transparent. I’d like to make my structs non-transparent for better encapsulation, but in the end I always make them transparent because I can compare two structs with equal? in automated tests.

Is there a not-too-obscure workaround to have the cake (better encapsulation) and it eat it (easy comparison in tests)? :slightly_smiling_face:


chansey97
2022-3-19 20:38:07

BTW, non-transparent struct also can be pattern match, that is surprising.


chansey97
2022-3-19 20:39:14

Typically, encapsulation means we can arbitrarily change fields’ order, i.e. we can not do pattern matching.


laurent.orseau
2022-3-19 21:05:22

encapsulation is overrated. Also, it’s annoying when one wants to build on top of a lib. It’s like unnecessary obfuscation. I remember mflatt saying that one mistake in struct was to not be #:transparent by default.


chansey97
2022-3-19 21:28:40

A peculiar feature of match in Racket: #lang racket (struct my-struct (x) #:transparent) (define (my-integer? x) (printf "my-integer? x=~a\n" x) (integer? x)) (define (my-add10 x) (printf "my-add10 x=~a\n" x) (+ x 10)) (match (my-struct 1) [(my-struct (app my-add10 (? my-integer? x))) (printf "x=~a\n" x)]) ;; my-add10 x=1 ;; my-integer? x=11 ;; x=11 When matching, the value will first call my-add10 (e.g. do some data transformation), then call predicate my-integer?.


ben.knoble
2022-3-19 22:05:21

Right, because the semantics of (app f pats…) patterns are (something like) “match a single value, apply f to it, and match the result with pats… . It looks weird because of the nested parens (we think inner-evaluation first), but it’s a match pattern, so the semantics are different.


sorawee
2022-3-19 23:18:23

In Rhombus, IIUC, there will be both class and Map. Map is essentially transparent struct.