Skip to content

Instantly share code, notes, and snippets.

@mofas
Last active September 3, 2018 20:23
Show Gist options
  • Save mofas/722e859cfdb3b45aa7b513e78d2ff7a0 to your computer and use it in GitHub Desktop.
Save mofas/722e859cfdb3b45aa7b513e78d2ff7a0 to your computer and use it in GitHub Desktop.
Macro presentation
;; 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