-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathex10-oop.rkt
141 lines (114 loc) · 3.69 KB
/
ex10-oop.rkt
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
125
126
127
128
129
#lang racket
;; Object-Oriented Programming
;; The return of CLOSURES
;; we can use closures to scope attributes and methods
;; to an instance.
;; We want to make an equivalent of the following class
#|
class Person {
public name: string
private age: int
// constructor
Person(name, age) {
this.name = name
this.age = age
}
int growOlder(int howManyYears) {
this.age += howManyYears
}
}
Person bob = Person(Bob, 25)
bob.getName()
(define bob ...)
(bob 'name) => "Bob"
|#
(define (new-person
initial-name ;; initial values / constructor
initial-age)
;; attributes
(let ([name initial-name]
[age initial-age])
;; methods
(define (get-name) ; getter for public attribute
name)
(define (grow-older years) ; a method to change age (and return it)
(set! age (+ age years))
age)
(define (show) ; another method
(display "Name: ")(displayln name)
(display "Age: ")(displayln age))
;; dispatcher (to handle calls to methods)
(λ (message . args)
(apply (case message
[(get-name) get-name]
[(grow-older) grow-older]
[(show) show]
[else (error "unknown method")])
args))))
(define ada (new-person "Ada" 25))
(define bob (new-person "Bob" 25))
(ada 'grow-older 10) ; => 35
(bob 'get-name) ; => "Bob"
(ada 'show)
(bob 'show)
;; Inheritance
(define (new-superhero name age init-power)
(let ([parent (new-person name age)] ; inherits attrs/methods
[power init-power])
(define (use-power)
(printf "~a uses ~a!\n" name power))
(define (show)
(parent 'show)
(display "Power: ")(displayln power))
(λ (message . args)
(case message
[(use-power) (apply use-power args)]
[(show) (apply show args)] ; overrides Person.show
[else (apply parent (cons message args))]))))
(define superman (new-superhero "Clark Kent" 32 "Flight"))
(superman 'use-power)
(superman 'grow-older 10)
(superman 'show)
;; PROTOTYPE-BASED OBJECTS
; under the hood, it's just an hashmap
; with attrs/method names as KEY and
; their value/implementation as VALUE
(define new-obj make-hash)
(define clone hash-copy)
;; We need to use MACROS because otherwise we cannot
;; quote 'msg' correctly: if we quote 'msg' at runtime
;; we are quoting 'msg' itself: 'msg
;; When we use a macro
;; BTW: define-syntax-rule allows you to define a macro
;; that binds just one single pattern.
;; SETTER
(define-syntax-rule (obj-set object msg new-val)
(hash-set! object 'msg new-val))
;; GETTER
(define-syntax-rule (obj-get object msg)
(hash-ref object 'msg))
;; SEND MESSAGE
(define-syntax-rule (obj-send object msg arg ...)
((hash-ref object 'msg) ; retrieve method
object arg ...)) ; call method with the object itself as first argument, and any arg
(define carl (new-obj))
(obj-set carl name "Carl")
(obj-set carl show
(λ (self)
(display "Name: ")(displayln (obj-get self name))))
(obj-set carl say-hi
(λ (self to)
(display (obj-get self name))(display " says hi to ")(displayln to)))
(obj-send carl show)
(obj-send carl say-hi "Dan")
; let create a new object that copies the properties of carl
(define dan (clone carl))
(obj-set dan name "Dan") ; this could be a setter method, if you want
(obj-send dan show)
(obj-set dan dance (λ (self)
(printf "I am ~a and I can dance" (obj-get self name))))
(obj-send dan dance)
; (obj-send carl dance) ; => error
;; This is a possible way to define prototypes.
;; Take a look at https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes
;; to see how JavaScript implements prototypes.