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?
what do you mean by “add new types later”?
Add a new type of account
@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?
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.
ok then just using (or/c 'current 'savings)
should work
Ah cool, I was hoping it would be easy.
I started to read the contracts documentation and my mind fuzzed over a bit.
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?
@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))
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
...
(prefix:make-id patchable-expr ...)
...
and then goes on to talk a lot about transformer bindings.
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;”
Agree that the wording “whose name has make- in the middle” is odd.
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.
that sounds plausible
cool. that’s easier than I thought. Thank you @soegaard2.
the wording is odd because it covers 3 different things and features an ugly hack
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!
@cawright.99 do you know that (define (name arg ...) body ...)
is a shorthand for (define name (lambda (arg ...) body ...))
?
@zenspider yep… I’ll go and replace the defines with that “expansion” and see what I can see :slightly_smiling_face: thanks
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.
I’m sure there is way more to it than that… but that’s the gist of it in this case.
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.
thanks very much
@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.
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!
Plenty more to do - simple GUI next
@cawright.99 For inspiration, you may be interested in http://cs.brown.edu/~sk/Publications/Talks/SwineBeforePerl/ and http://docs.racket-lang.org/automata/index.html
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.