# todo.rkt -rw-r--r-- 5.4 KiB View raw
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#lang racket

;; Ok! So say we want to be able to track a todo list with some goblins magic.

(require goblins
         goblins/actor-lib/methods
         racket/struct)


(define a-vat
  (make-vat)) ;; our code needs somewhere to live, thanks to goblins!

;; OK, so in goblins, we need to think about what we want to be able to do with data, not what the data actually is. 

;; So! Let's make a function that will give us the capabilites to edit and read a list of todos!
(define (spawn-todo-reader-and-writer)
  (define todos (spawn ^cell '())) ;; Here's a big list of all the todos. Note that I can't actually get at this from the normal environment.

  (struct todo (id value completed) ;; We need todos to track some info, so here we have 3 fields: id (a number), value (the thing to do), and completed (a bool to track the status)!
    #:methods gen:custom-write
    [(define write-proc ;; Over here we'll write some racket to make pretty printing work.
       (make-constructor-style-printer
        (lambda (obj) 'todo)
        (lambda (obj) (list (todo-id obj) (todo-value obj) (todo-completed obj)))
        )
       )
     ]
    )

  (define counter (spawn ^cell 0)) ;; I don't want todos to have the same index, so let's make a counter!

  (define (^reader _bcom) ;; A reader can read all the todos, just like we want.
    (methods
     [ (read-todos) ($ todos) ] ;; Because `todos` is a spritely object, we need to tell the computer I're acting on it! (the `$` operation)
     )
    )

  (define (^editor _bcom) ;; An editor can edit all the todos, because of course we need to be able to update them!
    (methods
     [(update-item index new-completed) ;; this will set the todo list to a todo list with the newer value
      (define old-todos ($ todos))
      (define old-item (list-ref old-todos index))
      ($ todos (
                list-set old-todos index ;; Set the todo at the index that we want to update to...
                         (todo ;; a todo ...
                          (todo-id old-item) ;; with the same id ...
                          (todo-value old-item) ;; same value ...
                          new-completed) ;; and the new completion!
                         )
         )
      ]

     [(add-item item) ;; Just slap a new todo onto there!
      (define old-todos ($ todos)) ;; Get the old values
      (define old-counter ($ counter)) ;; Get the old counter
      ($ counter (+ old-counter 1)) ;; Increment the counter
      ($ todos (append old-todos (list (todo (+ old-counter 1) item #f))))] ;; add the new todo
     )
    )

  ;; So now that we have the actually buisness, we need ot actually give the end user something! So here we give ...
  (define editor (spawn ^editor)) ;; the capability to edit all the todos ...
  (define reader (spawn ^reader)) ;; and the capability to read the todos!

  (values editor reader) ;; Then, return!
  )







(a-vat 'run 
       (lambda () ;; To intereact with these, let's go into our vat.
         (define-values (todo-editor todo-reader) (spawn-todo-reader-and-writer)) ;; Of course we need a list of todos
         ($ todo-editor 'add-item "finish code")
         ($ todo-editor 'add-item "write blog post")
         ($ todo-editor 'add-item "shop for groceries") ;; Let's add some stuff to do!
         (println ( $ todo-reader 'read-todos ))

         ;; $- '(#<todo: 1 "finish code" #f> #<todo: 2 "write blog post" #f> #<todo: 3 "shop for groceries" #f>)
         ;; Wow, look at that!


         ;; A bit later... and we've finished the blog post!
         ($ todo-editor 'update-item 1 #t) ;; (Don't forget to index from 0!)
         (println ( $ todo-reader 'read-todos ))
         ;; $- '(#<todo: 1 "finish code" #f> #<todo: 2 "write blog post" #f> #<todo: 3 "shop for groceries" #t>)

         ;; Well hm. I want my partner to be able to update a specific task, but I don't want them to accidentally overwrite all my other todos!
         ;; We should write some code to fix this :3

         (define (spawn-editor-for-one-item index editor) ;; So we need an editor for one thing, that means that we need to have an editor that can edit that one thing to start with! as well as the thing we want to edit.
           (define (^single-editor _bcom index) ;; here's that editor, we don't need to be able to spawn it outside this function, so we'll define it here.
             (methods
              [(update-item value) ;; all it needs to be able to do is update one thing
               ($ editor 'update-item index value) ;; index is hard-coded into this call! that means that my partner can't just edit anything.
               ]
              )
             )

           (define s-editor (spawn ^single-editor index)) ;; create the new editor
           (values s-editor) ;; and give it back!
           )

         (define-values (grocery-editor-for-partner) (spawn-editor-for-one-item 2 todo-editor)) ;; look! Now they can edit index number zero only.
         ;; Perfect! Now we can give them that capability, the one that is restriced. 


         ;;; Now we're over on their end after they shop! (Note, this is all local and doesn't have netcode yet.)

         ($ grocery-editor-for-partner 'update-item #t)

         ;;; Back at home....

         ;; After working for a while, I want to see what's happening again. So I check:
         (println ( $ todo-reader 'read-todos ))

         ;; $- '(#<todo: 1 "finish code" #f> #<todo: 2 "write blog post" #f> #<todo: 3 "shop for groceries" #t>)
         ;; Oh perfect! They finished the shopping. :3

         ))