Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement Racket-style struct in scheme?

In Racket you have the opportunity to create and use your structure type like this

(define-struct example (a b))

(define var (make-example 1 2))

(example? var)

(example-a var)

I'm trying to implement something like this in scheme using macros but I have a problem with the creation of procedures with combined names like (make-example 1 2) where instead of example can be absolutely anything. Is there a way to define procedures with such names or some other way to solve this problem?

like image 778
Vladislav Avatar asked Nov 04 '17 09:11

Vladislav


1 Answers

Is it neccesary to implement for Scheme?

There is no need for looking at other languages for features that already exists. It only has a different name: records!

It's defined in SRFI-9 Defining record types and the latest R7RS included backward compatibility to this so it's all good to choose this even in R5RS. Many R5RS implementations has them included. R6RS has a different implementation of records that is not compatible and should be avoided at this time.

#!r6rs
;; You want to use SRFI-9 rather than the d\included define-record-type
(import (except (rnrs) define-record-type)
        (srfi :9))

(define-record-type :example
  (make-example a b)
  example?
  (a example-a set-example-a!)
  (b example-b set-example-b!))

(define var (make-example 1 2))
(example? var)  ; ==> #t
(example-a var) ; ==> 1

You want to try anyway for the learning and the pleasure

Then the best way is to try figuring this out by yourself. To make a macro that makes concatenated identifiers you cannot use define-syntax since it cannot do that. Doing it without cheating too much will give you a better understanding of macros. In R6RS syntax-case was in the included libraries and you could make an identifier with datum->syntax:

#!r6rs
(import (rnrs)
        (rnrs syntax-case))

(define-syntax make-predicate
  (lambda (stx)
    (define (s->p sym)
      (string->symbol (string-append (symbol->string sym) "?")))

    (syntax-case stx ()
      [(_ name)
       (with-syntax
           ([predicate (datum->syntax #'name (s->p (syntax->datum #'name)))])
         #'(define (predicate v)
             (and (pair? v)
                  (eq? (car v) 'name))))])))

(make-predicate circle)
predicate?                  ; ==> #<procedure-predicate?>
(predicate? '(predicate x)) ; ==> #t

If you are after doing this is #lang racket rather than standard Scheme I think syntax-case exists and is compatible with this. Also I would suggest you read Greg Hendershott's fear of macros for more racket specific features.

You just want to know how it's done

Well. If you look the the SRFI-9 spec it supplies implementation and even look at #lang racket implementation of struct at github or simply right click on the symbol in your DrRacket and choose "Open defining file" to open the source just like any other file. There are no secrets to this but it might be slightly more complicated than you bargained for.

NB: Note that define-struct is no longer the preferred form in #lang racket, but is provided just for backwards compatibility. struct is preferred.

like image 199
Sylwester Avatar answered Nov 17 '22 12:11

Sylwester