Last active
September 3, 2018 20:23
-
-
Save mofas/722e859cfdb3b45aa7b513e78d2ff7a0 to your computer and use it in GitHub Desktop.
Macro presentation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;; Let say we want to build a text adventure game like following. | |
;; You’re standing in a meadow. | |
;; There is a house to the north. | |
;; > north | |
;; You are standing in front of a house. | |
;; There is a door here. | |
;; > open door | |
;; The door is locked. | |
;; > | |
;; At first, we probably need to define some data structures for building game. | |
;; Here, we roughtly need environments, items in environments, and actions that can interact with items. | |
(struct verb | |
(aliases ; list of symbols | |
Desc ; string | |
transitive?)) ; Boolean | |
(struct thing | |
(name ; symbol | |
[state #:mutable] ; any value | |
actions)) ; list of verb–function pairs | |
(struct place | |
(desc ; string | |
[things #:mutable] ; list of things | |
actions)) ; list of verb–function pairs | |
;; Example: defining an action | |
(define south (verb (list 'south 's) "go south" #false)) | |
;; However, writing those code is quite verbose. | |
;; original meadow def | |
(define meadow (place "You’re in a meadow." | |
(list flower) | |
(list (cons south (lambda () desert))))) | |
;; we can write in this way by using Macro | |
;; it is called syntactic abstraction | |
(define-place meadow | |
"You’re in a meadow." | |
[flower] | |
([south desert])) | |
;; let define our own syntax | |
(define-syntax-rule (define-place id desc [thng] ([vrb expr])) | |
(begin | |
(define id (place desc | |
(list thng) | |
(list (cons vrb (lambda () expr))))) | |
(record-element! 'id id) ;; store id for future use, we ignore it for now. | |
)) | |
;; Another example to use macro syntax for defining verbs quickly. | |
;; we don't want to fill all detail manually. | |
;; we want write code as | |
(define-verbs all-verbs | |
[quit] | |
[north (= n) “go north”] | |
[knock _] | |
[get _ (= grab take) “take”]) | |
;; we define syntax for one verb | |
(define-syntax define-one-verb | |
(syntax-rules (= _) | |
[(one-verb id (= alias ...) desc) | |
(define id (verb (list 'id 'alias ...) desc #false))] | |
[(one-verb id _ (= alias ...) desc) | |
(define id (verb (list 'id 'alias ...) desc #true))] | |
[(one-verb id) | |
(define id (verb (list 'id ) (symbol->string ’id ) #false))] | |
[(one-verb id _) | |
(define id (verb (list 'id ) (symbol->string ’id ) #true))])) | |
;; using macros as syntax surgar to improve productivity. | |
(define-syntax-rule (define-everywhere id ([vrb expr ] ...)) | |
(define id (list (cons vrb (lambda () expr )) ...))) | |
;; We even can do more if we write a reader/parser to handle non S-exp | |
;; For example: | |
;; ===VERBS=== | |
;; north, n | |
;; “go north” | |
;; get _, grab _, take _ | |
;; “take” | |
;; .... | |
;; ===EVERYWHERE=== | |
;; save | |
;; (save-game) | |
;; load | |
;; (load-game) | |
;; PROGRAMMING LANGUAGES | |
;; 13 | |
;; .... | |
;; ===THINGS=== | |
;; ---cactus--- | |
;; get | |
;; “Ouch!” | |
;; .... | |
;; ===PLACES=== | |
;; ---desert--- | |
;; “You’re in a desert.” | |
;; [cactus, key] | |
;; north start | |
;; south desert | |
;; .... | |
;; Macros not only can be used to improve productivity. | |
;; It can help developer write more secure code by introducing type or static check mechansim on compile time. | |
(begin-for-syntax | |
(struct typed (id type) | |
#:property prop:procedure (lambda (self stx) (typed-id self)) | |
#:omit-define-syntaxes)) | |
;; now we use Macro to define type system | |
;; check type | |
(define-syntax (check-type stx) | |
(syntax-case stx () | |
[(check-type id type) | |
(let ([v (and (identifier? #'id) | |
(syntax-local-value #'id (lambda () #f)))]) | |
(unless (and (typed? v) | |
(equal? (syntax-e #'type) (typed-type v))) | |
(raise-syntax-error | |
#f | |
(format "not defined as ~a" (syntax-e #'type)) | |
#'id)) | |
#'id)])) | |
(define-syntax-rule (define-everywhere id ([vrb expr] ...)) | |
(define id (list (cons (check-type vrb "intransitive verb") | |
(lambda () expr)) | |
...))) | |
;; define the type | |
(define-syntax-rule | |
(define-place id ....) | |
(begin | |
(define gen-id (place ....)) | |
(define-syntax id (typed #'gen-id "place")) | |
(record-element! 'id id ))) | |
;; check the type when you create a new data | |
(begin | |
(define gen-id | |
(place desc | |
(list (check-type thng "thing") ...) | |
(list (cons (check-type vrb "intransitive verb") | |
(lambda () expr )) ...))) | |
(define-syntax id (typed #'gen-id "place")) (record-element! 'id id ))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment