Curly brackets {} to replace 'begin' in Racket

拈花ヽ惹草 提交于 2019-12-10 15:23:24

问题


Is it possible to have a macro to use curly brackets {} to indicate a block of statments, so as to replace the 'begin' keyword. Hence, instead of:

(if (condition)
    (begin 
        (statement1)
        (statement2)
        (statement3)
        (statement4))
    (else-statement))

we may use:

(if (condition) {
        (statement1)
        (statement2)
        (statement3)
        (statement4) }
    (else-statement))

How can this be accomplished? Thanks for your answers.


回答1:


This is completely possible, and there are several ways to do it. (Quick note before I start, I'm going to use block instead of begin because it behaves better with internal definitions.)

Method 1: Re-defining #%app

One slightly hack-y way is to redefine what function application means so that curly-braces are treated specially. You can do this by defining an #%app macro:

#lang racket
(require racket/block syntax/parse/define (prefix-in - racket))
;; This #%app macro redefines what function application means so that
;; { def-or-expr ... } expands into (block def-or-expr ...)
;; Otherwise it uses normal function application
(define-syntax-parser #%app
  [{_ def-or-expr:expr ...}
   #:when (equal? #\{ (syntax-property this-syntax 'paren-shape))
   ;; group them in a block
   #'(block def-or-expr ...)]
  [(_ f:expr arg ...)
   #:when (not (equal? #\{ (syntax-property this-syntax 'paren-shape)))
   ;; expand to the old #%app form, from (prefix-in - racket)
   #'(-#%app f arg ...)])
;; using it:
(define (f x)
  (if (< 5 x) {
        (define y (- x 5))
        (f y)
      }
      x))
(f 1) ; 1
(f 5) ; 5
(f 6) ; 1
(f 10) ; 5
(f 11) ; 1

Method 2: Extending the Reader

Another way would be to define a new #lang language and extend the readtable with a different entry for the { character. Let me go and do that ...

To define a #lang language, you need to put the reader implementation in your-language/lang/reader.rkt. Here that's curly-block/lang/reader.rkt, where the curly-block directory is installed as a single-collection package (raco pkg install path/to/curly-block).

curly-block/lang/reader.rkt

;; s-exp syntax/module-reader is a language for defining new languages.
#lang s-exp syntax/module-reader
racket
#:wrapper1 (lambda (th)
             (parameterize ([current-readtable (make-curly-block-readtable (current-readtable))])
               (th)))

;; This extends the orig-readtable with an entry for `{` that translates
;; { def-or-expr ... } into (block def-or-expr ...)
(define (make-curly-block-readtable orig-readtable)
  (make-readtable orig-readtable
    #\{ 'terminating-macro curly-block-proc))

;; This is the function that the new readtable will use when in encounters a `{`
(define (curly-block-proc char in src ln col pos)
  ;; This reads the list of things ending with the character that closes `char`
  ;; The #f means it uses the racket reader for the first step, so that `{`
  ;; uses the normal behavior, grouping expressions into a reader-level list
  (define lst (read-syntax/recursive src in char #f))
  (cons 'block lst))

Using it:

#lang curly-block
(require racket/block)
(define (f x)
  (if (< 5 x) {
        (define y (- x 5))
        (f y)
      }
      x))
(f 1) ; 1
(f 5) ; 5
(f 6) ; 1
(f 10) ; 5
(f 11) ; 1


来源:https://stackoverflow.com/questions/38369817/curly-brackets-to-replace-begin-in-racket

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!