On 2011-05-22, at 4:10 PM, Matthew Koichi Grimes wrote:
On Sun, May 22, 2011 at 6:39 AM, Mikael mikael.rcv@gmail.com wrote:
Why would you want this?
As Adrien mentioned, it's a common practice in other languages, which I got used to (Python: override __repr__(), c++: override operator<<(), Java: override ToString(), etc). In my particular case, I was writing a doubly-linked list type, and wanted it to print (up to) its first three and last three values when displayed in the REPL.
Anyhow, you could make your own Gambit patch that adds a pp: procedure argument to define-type. Feel free to publish your patch on the Dumping grounds.
I'll start by learning how to write macros! :D
Alternatively you can add a hook for your types in the "printer:
(define-type foo field1 field2)
(set! ##wr (lambda (we obj) (cond ((foo? obj) (##wr-str we "#s(FOO ") (##wr we (foo-field1 obj)) (##wr-str we " ") (##wr we (foo-field2 obj)) (##wr-str we ")")) (else (##default-wr we obj)))))
(pp (make-foo 11 22)) ;; prints: #s(FOO 11 22)
Similarly, how can I define a custom constructor that does something more complicated than assign arguments to slots?
By defining your own procedure that does the job.
I figured it out. Here's an expanded answer for other beginners. It's actually as easy as:
(define-type my-type ... constructor: (lambda (<args>) <body>) ...)
That's not correct. The constructor: clause specifies the name of the constructor to override the default name, i.e.
(define-type foo constructor: make-bar field1 field2)
(pp (make-bar 11 22))
You want something like this:
(define-type foo field1 field2)
(define (construct-a-foo x) (make-foo x (* x x)))
Someone else please verify that Gambit's define-record-type is just an alternative name for define-type, just like what call/cc is to call-with-current-continuation .
It seems that they aren't equivalent. Gambit's define-record-type comes from srfi-9, which uses positional arguments rather than define-type's keyword arguments:
http://srfi.schemers.org/srfi-9/srfi-9.html
From the example in the above link, we see that you can define a "pare" (pair) type as follows: (define-record-type :pare
(kons x y) pare? (x kar set-kar!) (y kdr))
We can then do a quick smoke-check to see that it worked:
(pare? (kons 1 2))
#t
If we restart the interpreter and repeat the above two steps, this time using define-type instead of define-record type, we get:
(define-type :pare (kons x y) pare? (x kar set-kar!) (y kdr))
(pare? (kons 1 2))
*** ERROR IN (console)@2.2 -- Unbound variable: pare?
That said, define-record-type does seem to be defined in terms of define-type, as can be seen in ~~/lib/nonstd.scm. Also, define-structure is equivalent to define-type:
(define-runtime-macro (define-type . args) (##define-type-expand 'define-type #f #f args))
(define-runtime-macro (define-structure . args) (##define-type-expand 'define-structure #f #f args))
(define-runtime-macro (define-record-type name constructor predicate . fields) `(define-type ,name constructor: ,constructor predicate: ,predicate ,@fields))
Yes all three forms share a common implementation. So wether you define a structure with a define-type or define-structure or define-record-type (with their respective syntaxes) the same implementation is obtained.
Marc