2

I have following class which works fine:

(define myob%
  (class object%
    (super-new)
    (init-field val)
    (define/public (getval) val)
    (define/public (setval v) (set! val v))   ))

(define ob1 (make-object myob% 5))

(send ob1 getval)
(send ob1 setval 10)
(send ob1 getval)

Output:

5
10

Following regex also works well:

(define sl (regexp-match #px"^(.+)[.]([^.]+)$" "ob1.getval"))
sl

Output:

'("ob1.getval" "ob1" "getval")

I am trying to make a fn foo which should work like 'send' but take arguments in form of (foo ob1.getval) or (foo ob1.setval 10) . Following macro is not working:

(define-syntax foo
    (syntax-rules ()
      ((_ sstr ...)
       (define sl (regexp-match #px"^(.+)[.]([^.]+)$"
                                (symbol->string sstr)))
       (send (string->symbol(list-ref sl 1))
             (string->symbol(list-ref sl 2))
             ...))))

(foo ob1.getval)

The error is:

syntax-rules: bad syntax in: (syntax-rules () ((_ sstr ...) (define sl (regexp-match #px"^(.+)[.]([^.]+)$" (symbol->string sstr))) (send (list-ref sl 1) (list-ref sl 2) ...)))

Where is the error and how can this be corrected?

rnso
  • 23,686
  • 25
  • 112
  • 234

1 Answers1

5

To use dot notation like this, you'll need to define a new #lang language, with its own reader. There are several tools to help with this, and I'll be using one of them, syntax/module-reader, to define #lang send-dot, which once defined can be used like this:

#lang send-dot

(define o
  (new (class object% (super-new)
         (define/public (f x) x))))

(o.f "hellooo")

In the latest snapshot version of Racket, you can use the read-cdot option. Make sure you're on the latest snapshot version, since in 6.6, it's completely broken.

One way to define a #lang is by declaring a reader submodule. Make a directory called send-dot, and add a file called main.rkt. This file should provide everything from racket.

#lang racket

(provide (all-from-out racket))

This doesn't define a #lang yet. But to try it out, you can go to DrRacket's File menu, click on Package Manager, and in the Package Source field, enter the path to the send-dot directory. Once you do that, you should be able to use #lang s-exp send-dot in another file just like you would #lang racket.

To define a reader for this language and make it a real #lang language, you can add a reader submodule that uses syntax/module-reader as its language.

#lang racket

(provide (all-from-out racket))

;; This submodule defines the reader for the language
(module reader syntax/module-reader
  send-dot)

Now you should be able to use #lang send-dot just like #lang racket.

Now you need to do two more things. One, turn on the read-cdot option so that (o.method args ...) is translated to ((#%dot o method) args ...), and Two, define a #%dot macro so that ((#%dot o method) args ...) is equivalent to (send o method args ...).

For the first thing, you can use the #:wrapper1 option, using parameterize to turn read-cdot on.

#lang racket

(provide (all-from-out racket))

;; This submodule defines the reader for the language
(module reader syntax/module-reader
  send-dot
  #:wrapper1 (lambda (thunk)
               ;; turns on the read-cdot option,
               ;; which will turn o.method into (#%dot o method),
               ;; and (o.method args ...) into ((#%dot o method) args ...)
               (parameterize ([read-cdot #true])
                 (thunk))))

For the second thing, you need to define a #%dot macro. o.method, or (#%dot o method), needs to be a function that calls the method, so you can use (lambda args (send/apply o method args)).

#lang racket

(provide #%dot (all-from-out racket))

;; transforms (#%dot o method) into a function that calls the method
;; so that ((#%dot o method) args ...) will be roughly equivalent to
;; (send o method args ...)
(define-syntax-rule (#%dot obj-expr method-id)
  (let ([obj obj-expr])
    (lambda args (send/apply obj method-id args))))

;; This submodule defines the reader for the language
(module reader syntax/module-reader
  send-dot
  #:wrapper1 (lambda (thunk)
               ;; turns on the read-cdot option,
               ;; which will turn o.method into (#%dot o method),
               ;; and (o.method args ...) into ((#%dot o method) args ...)
               (parameterize ([read-cdot #true])
                 (thunk))))

Now you should be able to use #lang send-dot like this:

#lang send-dot

(define o
  (new (class object% (super-new)
         (define/public (f x) x))))

(o.f "hellooo")
Alex Knauth
  • 8,133
  • 2
  • 16
  • 31
  • Great. Can you briefly add here itself how dot can also be used for structure elements in this new language (with reference to http://stackoverflow.com/questions/39145000/macro-to-use-dot-notation-to-get-structure-fields-in-racket ) – rnso Aug 30 '16 at 14:25
  • Objects are easier because they can rely on dynamic dispatch. Structs would be a bit harder if you wanted `mydog.name`, but it might be easier if you wanted `mydog.dog-name` to be translated to `(dog-name mydog)` – Alex Knauth Aug 30 '16 at 14:30
  • 1
    Code to convert mydog.dog-name to (dog-name mydog) will be appreciated here. Or will it just work because read-cdot is active here. I believe it will have to be used without parentheses. Is that right? – rnso Aug 30 '16 at 15:16