@chris-lowen has joined the channel
Hi, what data type should I use if I want to associate some states with a function?
How do you represent the states?
@soegaard2 hmm, I don’t understand the question.
Do you store the states as integers, symbols, structs something else?
hmm, ideally, it could be anything. how does it matter?
Well, if you states were an integer between 1 and 10, then a vector would be ideal.
If states can be anything, then look at hash-tables.
Say we use hash table, then how to pair the state with functions?
Functions (represented as closures) are normal values, so you can store them directly in the hash table.
#lang racket
(define ht (make-hash))
(hash-set! ht "plus" +)
(define (lookup name)
(hash-ref ht name #f))
((lookup "plus") 3 4)
The result of this is 7.
hmm that’s not what I want. Let me give more details.
I can describe how I would do it in OOP. Basically what I’m trying to make is similar to the Observer Pattern. 1, I keep a list of observers, then when something happens, I loop through the lisp of observers and call each of them with the event. 2, the observer should be an interface, so the implementation can have any states as it wants.
So you want to associate a state with a list of functions?
there are two problems here. 1, what do I use to keep the observers. I think a list
would do just fine.
2, what do I use for observers. This is my main question. An observer can have any states it wants, but to implement the interface, it could have one, or some functions.
But again, I think observer pattern is OOP. If there’s a FP way to solve the problem in racket, I’d love to learn.
(define observer-ht (make-hash))
(define (add-observer state observer)
(define existing-observers (hash-ref observer-ht state (set)))
(hash-set! observer-ht state (set-add existing-observers observer)))
(define (get-observers state)
(hash-ref observer-ht state (set)))
(define (call-observers state)
(define observers (get-observers state))
(for ([o observers])
(o state)))
(define (observer1 state)
(displayln (~a "Observer 1 has seen " state)))
(define (observer2 state)
(displayln (~a "Observer 2 has seen " state)))
(add-observer "state1" observer1)
(add-observer "state1" observer2)
(call-observers "state1")
@markx Here is a longer example.
@soegaard2 Thanks for the example. I think it mains shows how to solve the first problem, ie how to manager the observers. But my questions is more about what to use for the observers.
In the examples, the observers are just functions, so they don’t have states, right?
The original question was how to associate states to functions.
Right. The observers are functions, but I want the ability to associate states with them.
Use another hash table.
Hmm, I think I know now… I just use closure to save the states.s
Or represent observers as a struct containing both states and handlers to called.
SICP shows how to implement “propagation of constraints”
It sounds as you want something in that ball park.
(define count 0)
(lambda (event)
(set! count (+ count 1))
(printf "I count ~a\n" count)))
(define ob (make-counter-observer))
(ob "some event")
(ob "another event")
nah I think it’s a little too hard to read that for me right now. Later I’ll try to read sicp again, but not now…
@soegaard2 I don’t see how struct can help on this one.
Can you give a concrete examples of states and observers?
forget about the states… the states I’m talking about are within the observers.
Basically, I want the ability to have both pure functions, and state machines, as observers.
One example would just be the counter-observer
I posted above. It tracks how many events have happened so for.
I know that there’s an OOP system in racket, which should work. But I’m trying to see if there’s another way, or a better way, to do this.
Okay, so we a counter. And when the counter changes, you want the system to automatically invoke some observers?
No. The counter is a local state to the observer, and the system shouldn’t do anything about it.
What is the observer observing then?
events.
So something like this? (example from SICP): (define (make-account balance)
(define (withdraw amount)
(if (>= balance amount)
(begin (set! balance (- balance amount))
balance)
"Insufficient funds"))
(define (deposit amount)
(set! balance (+ balance amount))
balance)
(define (dispatch m)
(cond ((eq? m 'withdraw) withdraw)
((eq? m 'deposit) deposit)
(else (error "Unknown request -- MAKE-ACCOUNT"
m))))
dispatch)
And it can be used like this: (define acc (make-account 100))
((acc 'withdraw) 50)
50
((acc 'withdraw) 60)
"Insufficient funds"
((acc 'deposit) 40)
90
((acc 'withdraw) 60)
30
Yeah but it’s the same as my counter observer. It’s using the closure to keep the state.
Yes. And ’withdraw and ’deposit are the events.
Is there a better way to do this? other than using closure?
I think I’d just use an object (but you are trying to find other solutions).
object, as the object in OOP?
yes
Then I guess you mean a class, not an object.
I do need a class to make the object. The object plays the same role as the closure. Anyways, I think we agree.
Yeah.
What does an interface
in racket do? Racket has dynamic typing, right? So why do we need interface
?
>This use of interfaces is helpful even without a static type system
“How do I X?” “You do ABC” “No. Your answer is wrong. Forget about X.” ¯_(ツ)_/¯
haha @greg Do you have any insight?
Just realized that DrRacket colors mutated variables red. Now I can’t pretend I wasn’t warned…
I guess generally I should try to avoid using classes in racket, but I find it very difficult.
My brain naturally models everything in classes and objects, and it’s not obvious to me how to solve the problems using just lists.