chris-lowen
2018-8-4 13:18:07

@chris-lowen has joined the channel


markx
2018-8-4 14:49:34

Hi, what data type should I use if I want to associate some states with a function?


soegaard2
2018-8-4 14:50:11

How do you represent the states?


markx
2018-8-4 14:55:29

@soegaard2 hmm, I don’t understand the question.


soegaard2
2018-8-4 14:55:51

Do you store the states as integers, symbols, structs something else?


markx
2018-8-4 14:56:13

hmm, ideally, it could be anything. how does it matter?


soegaard2
2018-8-4 14:56:47

Well, if you states were an integer between 1 and 10, then a vector would be ideal.


soegaard2
2018-8-4 14:56:58

If states can be anything, then look at hash-tables.


markx
2018-8-4 14:57:47

Say we use hash table, then how to pair the state with functions?


soegaard2
2018-8-4 14:58:33

Functions (represented as closures) are normal values, so you can store them directly in the hash table.


soegaard2
2018-8-4 15:01:23

#lang racket (define ht (make-hash)) (hash-set! ht "plus" +) (define (lookup name) (hash-ref ht name #f)) ((lookup "plus") 3 4)


soegaard2
2018-8-4 15:01:32

The result of this is 7.


markx
2018-8-4 15:02:07

hmm that’s not what I want. Let me give more details.


markx
2018-8-4 15:02:09

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.


soegaard2
2018-8-4 15:02:45

So you want to associate a state with a list of functions?


markx
2018-8-4 15:03:24

there are two problems here. 1, what do I use to keep the observers. I think a list would do just fine.


markx
2018-8-4 15:04:41

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.


markx
2018-8-4 15:08:29

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.


soegaard2
2018-8-4 15:12:04
(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")

soegaard2
2018-8-4 15:12:24

@markx Here is a longer example.


markx
2018-8-4 15:15:00

@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.


markx
2018-8-4 15:15:33

In the examples, the observers are just functions, so they don’t have states, right?


soegaard2
2018-8-4 15:17:10

The original question was how to associate states to functions.


markx
2018-8-4 15:19:39

Right. The observers are functions, but I want the ability to associate states with them.


soegaard2
2018-8-4 15:20:56

Use another hash table.


markx
2018-8-4 15:21:17

Hmm, I think I know now… I just use closure to save the states.s


soegaard2
2018-8-4 15:21:41

Or represent observers as a struct containing both states and handlers to called.



soegaard2
2018-8-4 15:22:48

SICP shows how to implement “propagation of constraints”


soegaard2
2018-8-4 15:23:08

It sounds as you want something in that ball park.


markx
2018-8-4 15:26:17
  (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")

markx
2018-8-4 15:31:53

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…


markx
2018-8-4 15:33:19

@soegaard2 I don’t see how struct can help on this one.


soegaard2
2018-8-4 15:33:45

Can you give a concrete examples of states and observers?


markx
2018-8-4 15:34:34

forget about the states… the states I’m talking about are within the observers.


markx
2018-8-4 15:35:16

Basically, I want the ability to have both pure functions, and state machines, as observers.


markx
2018-8-4 15:36:15

One example would just be the counter-observer I posted above. It tracks how many events have happened so for.


markx
2018-8-4 15:38:18

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.


soegaard2
2018-8-4 15:39:33

Okay, so we a counter. And when the counter changes, you want the system to automatically invoke some observers?


markx
2018-8-4 15:42:49

No. The counter is a local state to the observer, and the system shouldn’t do anything about it.


soegaard2
2018-8-4 15:43:51

What is the observer observing then?


markx
2018-8-4 15:52:46

events.


soegaard2
2018-8-4 15:57:51

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)


soegaard2
2018-8-4 15:59:15

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


markx
2018-8-4 16:05:11

Yeah but it’s the same as my counter observer. It’s using the closure to keep the state.


soegaard2
2018-8-4 16:05:34

Yes. And ’withdraw and ’deposit are the events.


markx
2018-8-4 16:06:03

Is there a better way to do this? other than using closure?


soegaard2
2018-8-4 16:07:12

I think I’d just use an object (but you are trying to find other solutions).


markx
2018-8-4 16:09:11

object, as the object in OOP?


soegaard2
2018-8-4 16:09:20

yes


markx
2018-8-4 16:09:43

Then I guess you mean a class, not an object.


soegaard2
2018-8-4 16:11:45

I do need a class to make the object. The object plays the same role as the closure. Anyways, I think we agree.


markx
2018-8-4 16:12:53

Yeah.


markx
2018-8-4 16:14:06

What does an interface in racket do? Racket has dynamic typing, right? So why do we need interface?



markx
2018-8-4 16:28:34

>This use of interfaces is helpful even without a static type system


greg
2018-8-4 17:01:06

“How do I X?” “You do ABC” “No. Your answer is wrong. Forget about X.” ¯_(ツ)_/¯


markx
2018-8-4 17:02:50

haha @greg Do you have any insight?


soegaard2
2018-8-4 17:05:23

Just realized that DrRacket colors mutated variables red. Now I can’t pretend I wasn’t warned…


markx
2018-8-5 02:51:30

I guess generally I should try to avoid using classes in racket, but I find it very difficult.


markx
2018-8-5 02:52:13

My brain naturally models everything in classes and objects, and it’s not obvious to me how to solve the problems using just lists.