Skip to content

Instantly share code, notes, and snippets.

@gwangjinkim
Last active March 18, 2021 07:22
Show Gist options
  • Save gwangjinkim/77c9525a6dc46cc8014a3d3d4b484583 to your computer and use it in GitHub Desktop.
Save gwangjinkim/77c9525a6dc46cc8014a3d3d4b484583 to your computer and use it in GitHub Desktop.
Message Passing Style and OOP using Racket. About the advantages using OOP. (This code nicely shows how to implement OOP using closures. Attributes are nothing else than closed-over variables. Methods are functions returned from the closure.)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; notes from the book "Vom Problem zum Programm" by Herbert Klaeren and Michael Sperber
;; on object oriented programming
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; object oriented programming = message passing style + state + self + inheritance
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; object = active container in message passing style
;; active container = procedure with one argument (= message, a symbol)
;; returning a function (method)
;; method extractor function
(define get-method
(lambda (object message)
(object message))
;; example object person
;; person say stuff
;; when saying uncomfortable stuff, one can slap them.
;; constructor for a person
(define make-person
(lambda ()
(let ((slaps 0))
(letrec ((self (lambda (message)
(cond ((eq? message 'say)
(lambda (stuff)
(print-newline stuff)))
((eq? message 'slap)
(lambda ()
(set! slaps (+ 1 slaps))
(if (= slaps 3)
(begin
((self 'say) '(ouch!))
(set! slaps 0)))))
(else 'no-method)))))
self))))
(define print-newline
(lambda (stuff)
(for-each display stuff)
(newline)))
(define george (make-person))
((get-method george 'say) (list "hello"))
;; hello
((get-method george 'slap))
((get-method george 'slap))
((get-method george 'slap))
;; ouch!
(define send
(lambda (object message . args)
(let ((method (get-mmethod object message)))
(if (method? method)
(apply method args)
(error "No method" message)))))
(define method? procedure?)
(send george 'say (list "hello"))
;; hello
(send george 'slap)
(send george 'slap)
(send george 'slap)
;; ouch!
;; a singer is a person who as additional method sing
(define make-singer
(lambda ()
(let ((person (make-person)))
(letrec
((self
(lambda (mesage)
(cond ((eq? message 'sing)
(lambda (stuff)
(send self 'say (cons "tra-la-la " stuff))))
(else
(get-method person message)))))
self))))
(define claudia (make-singer))
(send claudia 'say (list "hello"))
;; hello
(send claudia 'sing (list "hello"))
;; tra-la-la hello
;; objects which can inherit are called classes (different than in typical OOP languages)
;; rockstar is a singer, who adds to everything what he says ", dude"
;; in addition he reacts differently to slaps
;; -> method overriding
(define make-rockstar
(lambda ()
(let ((singer (make-singer)))
(letrec ((self
(lambda (message)
(cond ((eq? message 'say)
(lambda (stuff)
(send singer 'say
(append stuff (list ", dude"))))
((eq? message 'slap)
(lambda ()
(send self 'say
(list "pain just makes me stronger"))))
(else
(get-method singer message))))))
self))))
(define lash (make-rock-star))
(send slash 'say (list "hello"))
;;=> hello, dude
(send slash 'slap)
;;=> pain just makes me stronger, dude
(send slahs 'sing (list "oh yeah"))
;;=> tra-la-la oh yeah
;; no addition of ', dude'
;; self should refer to the actual object, not its superclasses
(define make-person
(lambda ()
(let ((slaps 0))
(lambda (message)
(cond ((eq? message 'say)
(lambda (self stuff)
(print-newline stuff)))
((eq? message 'slap)
(lambda (self)
(set! slaps (+ 1 slaps))
(if (= slaps 3)
(begin
((self 'say) '(ouch!))
(set! slaps 0)))
(else 'no-method)))))
(define send
(lambda (object message . args)
(let ((method (get-method object message)))
(if (method? method)
(apply method (cons object args))
(error "No method" message)))))
(define make-singer
(lambda ()
(let ((person (make-person)))
(lambda (message)
(cond ((eq? message 'sing)
(lambda (self stuff)
(send self 'say (cons "tra-la-la " stuff))))
(else (get-method person message))))))
(define make-rock-star
(lambda ()
(let ((singer (make-singer)))
(lambda (message)
(cond ((eq? message 'say)
(lambda (self stuff)
(send singer 'say
(append stuff (list ", dude")))))
((eq? message 'slap)
(lambda (self)
(send self 'say
(list "pain just makes me stronger"))))
(else
(get-method singer message)))))))
(define slash (make-rock-star)
(send slash 'sing (list "oh yeah"))
;;=> tra-la-la oh yeah, dude
;; method overriding follows only the intuition if functionality is only added
;; but never subtracted. But this is not the case.
;; thus avoid method overriding
;; multiple inheritance
(define make-poet
(lambda ()
(lambda (message)
(cond ((eq? message 'say)
(lambda (self stuff)
(print-newline
(append stuff (list " and the sky is blue")))))
((eq? message 'recite)
(lambda (self)
(print-newline (list "the sky is blue"))))
(else
'no-method))))
(define johann (make-poet))
(send johann 'say (list "hello"))
;;=> hello and the sky is blue
(define axl
(let ((rock-star (make-rock-star))
(poet (make-poet)))
(lambda (message)
(let ((rock-star-method (get-method rock-star message)))
(if (method? rock-star-method)
rock-star-method
(get-method poet message))))))
(send axl 'say (list "hello"))
;;=> hello, dude
(send axl 'recide)
;;=> the sky is blue
(define henry
(let ((poet (make-poet))
(rock-star (make-rock-star)))
(lambda (message)
(let ((poet-method (get-method poet message)))
(if (method? poet-method)
poet-method
(get-method rock-star message))))))
(send henry 'say (list "hello"))
;;=> hello and the sky is blue
(send henry 'slap)
;;=> pain just makes me stronger and the sky is blue
;; mix-ins
(define make-make-cool-someone
(lambda (make-someone)
(lambda ()
(let ((someone (make-someone)))
(lambda (message)
(cond ((eq? message 'say)
(lambda (self stuff)
(send someone 'say
(append stuff (list ", dude")))))
((eq? message 'slap)
(lambda (self)
(send self 'say
(list "pain just makes me stronger"))))
(else (get-method someone message))))))))
(define rock-star
(make-make-cool-someone make-singer))
(define slash (make-rock-star))
(send slash 'sing (list "oh yeah"))
;;=> tra-la-la oy yeah, dude
(define make-cool-poet
(make-make-cool-someone make-poet))
(define charles (make-cool-poet))
(send charles 'say (list "hello"))
;;=> hello, dude and the sky is blue
;; make-make-cool-someone procedure abstracts the feature of coolness
;; which is applicable on all classes with a 'say'-method.
;; such a procedure, which has a class (constructor) as its argument
;; and returns a class, which is extended by certain features - is called `mixin`.
;; => isolate features or classes into mixins
;; 1. use mixins as possible
;; 2. avoid inheritance and method overriding
;; 3. the less knowledge is centralized, the better (use classes)
;; 4. avoid code changes - instead extend code (use classes)
;; use generic methods - use polymorphy if possible (and encapsulate the type-specific behavior into classes)
;; polymorphy is about using the same verb/word for things which correlate between the classes
;; this is brainfriendly and also a kind of abstraction
;; use polymorphy as possible
;; 5. use types for important data types in programs (use classes)
;; -> use polymorphy to abstract over different function on different data types
;; -> use different types for different data types
;; -> for using polymorphy and to sustain the principle that adding code is better than changing existing code
;; (to have to avoid to do changes on every generic function)
;; when adding a new type,
;; at the same time, to keep the code data-driven/type-driven (decentralized knowledge about behavior)
;; and also for using types for important kind of data,
;; one should use classes.
;; -> when using classes, avoid inheritance (because it is not intuitive and does not add code)
;; and because of method overriding -> leads to mutable class definitions
;; so when using inheritance, avoid method overriding
;; -> when using classes, use mixins whenever possible (abstract away features from classes)
;; because they are like methods are abstractions of functions
;; abstractions of parts/features of classes.
;; use classes
;; -> to distinguish different important data types
;; -> to use abstraction over corresponding actions on the different types (polymorphism)
;; -> to follow code/function addition principle (maintainability!)
;; (to only add code and functionality but not to delete/change existing code)
;; -> to follow decentralized knowledge principle over implementation details and types
;; -> to program data-driven
;; -> to keep name spaces clean / avoid name space collisions
;; -> for easier reusability of code
;; avoid inheritance, especially method overriding
;; use mixins
;; -> to abstract over polymorphisms and parts of classes
;; -> to even more decentralize knowledge
;; -> to even more follow code/function addition principle
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment