mark.warren
2018-10-24 10:23:40

This is a very contrived example but I’m trying to understand contracts and I can’t work this out. Basically I want to limit a value in a struct to only certain symbols #lang racket (struct account (type)) (provide (contract-out [struct account ((type symbol?))]))

If I want to limit type to be 'current or 'savings but with the ability to add new types later, how would I best do that?


samth
2018-10-24 13:33:57

what do you mean by “add new types later”?


mark.warren
2018-10-24 13:41:56

Add a new type of account


samth
2018-10-24 13:44:24

@mark.warren do you want to add a new type of account by changing the contract, or by calling a function in some other part of the code and having that affect the contract?


mark.warren
2018-10-24 13:45:55

I’m not explaining this well. I guess I mean a bit like Java enumeration types, so you could start with savings and current in that enumeration, but add another if you created a new type of account.


samth
2018-10-24 13:47:25

ok then just using (or/c 'current 'savings) should work


mark.warren
2018-10-24 13:47:49

Ah cool, I was hoping it would be easy.


mark.warren
2018-10-24 13:48:40

I started to read the contracts documentation and my mind fuzzed over a bit.


zenspider
2018-10-24 20:19:18

WRT my shared code above… can someone explain to me the make-X requirement for structs and why it bumps me from using plain struct to define-struct? Is there a better/easier way to do cyclic references than changing my structs around?


samth
2018-10-24 20:20:47

@zenspider I don’t understand what you mean about a requirement; this works for me: #lang racket (struct node (arc) #:mutable #:transparent) (struct arc (trigger destination) #:mutable #:transparent) (shared ([s1 (node (arc 'some-trigger s2))] [s2 (node (arc 'some-other-trigger s1))]) (values s1 s2))


zenspider
2018-10-24 20:21:30

it does?? I could have sworn I tried that and it blew up… and the doco is strangely worded such that I thought having actual naming of make-X was a requirement


zenspider
2018-10-24 20:23:34
...
    (prefix:make-id patchable-expr ...)
...

and then goes on to talk a lot about transformer bindings.


soegaard2
2018-10-24 20:23:39

Quoting: “The prefix:make-id identifier above matches three kinds of references. The first kind is any binding whose name has make- in the middle, and where prefix:id has a transformer binding to structure information with a full set of mutator bindings;”


soegaard2
2018-10-24 20:24:20

Agree that the wording “whose name has make- in the middle” is odd.


zenspider
2018-10-24 20:24:59

oh… I’m just guessing… but the original example didn’t have the structs as mutable, and I probably tweaked the struct type before that.


samth
2018-10-24 20:25:26

that sounds plausible


zenspider
2018-10-24 20:25:29

cool. that’s easier than I thought. Thank you @soegaard2.


samth
2018-10-24 20:25:55

the wording is odd because it covers 3 different things and features an ugly hack


cawright.99
2018-10-24 21:59:11

I am not sure I understand how forward references are dealt with. Actually, I’m pretty certain I don’t… #lang racket (define (foo x) (+ x baz)) (define baz 1) (foo 1) (struct node (arc) #:transparent) (struct arc (to-node) #:transparent) (define foo-node (node (arc bar-node))) (define bar-node (node (arc 'whatever)))

(foo 1) works, despite baz being defined “after” (in a top-down, left to right sense) foo. But (define foo-node ...) fails with bar-node: undefined; cannot reference an identifier before its definition

I don’t understand the difference between the two situations, and would like to!


zenspider
2018-10-24 22:02:08

@cawright.99 do you know that (define (name arg ...) body ...) is a shorthand for (define name (lambda (arg ...) body ...)) ?


cawright.99
2018-10-24 22:04:08

@zenspider yep… I’ll go and replace the defines with that “expansion” and see what I can see :slightly_smiling_face: thanks


zenspider
2018-10-24 22:05:20

the point being, that a lambda expression body isn’t evaluated when it is defined, but when it is called… whereas your foo-node and bar-node are being created when you define them.


zenspider
2018-10-24 22:05:43

I’m sure there is way more to it than that… but that’s the gist of it in this case.


cawright.99
2018-10-24 22:17:27

So when I’m doing (define foo-node (node (arc bar-node))), it’s just a set of function calls so that we can find the right binding for foo-node, and bar-node isn’t visible. But (define (foo x)... expands into the lambda form, and thus delayed until it’s run.


cawright.99
2018-10-24 22:17:33

thanks very much


philip.mcgrath
2018-10-25 01:14:50

@cawright.99 If you want a beautiful step-by-step visualization of how evaluation works, try the Stepper from the “Beginning Student Language” from How to Design Programs.

You need to slightly change your program to use BSL syntax: (define (foo x) (+ x baz)) (define baz 1) (foo 1) (define-struct node (arc)) (define-struct arc (to-node)) (define foo-node (make-node (make-arc bar-node))) (define bar-node (make-node (make-arc 'whatever))) Note that there is no #lang line! Instead, in DrRacket, click on “Determine language from source” in the bottom-left corner and change it to “Beginning Student”.

After you run your program so that the language change takes effect, DrRacket will have a “Step” button to the left of “Run”: this will show you how Racket evaluates your program. While Beginning Student Language is more restrictive than #lang racket, the evaluation model is the same, so the intuitions you develop from using the Stepper transfer to the full Racket language.


cawright.99
2018-10-25 02:18:18

OK - thanks heaps @philip.mcgrath - I’ll have a play around with it! I’ve managed to get where I wanted to get, and have managed to “scratch my itch”. I am building a fsm with timeouts, transitions with actions, and global mutable store. It’s working, and I’m very grateful for the help I’ve received!


cawright.99
2018-10-25 02:27:47

Plenty more to do - simple GUI next


philip.mcgrath
2018-10-25 05:50:47

cawright.99
2018-10-25 06:52:21

Thanks @philip.mcgrath. That article provided the original inspiration to do this in Racket - the stucture of the nodes is a bit more complex (with timeouts) and mutable global state (a trivial assoc list DB) also made the macros more complex. So I’ve used the macros to “parse” an (s-expr) representation, but then they build a data structure rather than mutually recursive functions.