[racket] How to either extend eval or write macros in #lazy?

From: Sean Kanaley (skanaley at gmail.com)
Date: Tue Jul 2 15:53:51 EDT 2013

Yes, but first I'll say the issue is with a nested define-syntax inside 
of a lazy begin (a macro-writing macro). I got it to sort of work by 
renaming racket/base begin to !begin and using that instead, but then 
the resulting usage of the functions in "user code" does not evaluate 
the promises that #lazy creates and require being wrapped by (! ...). 
Here is the macro-writing-macro:

(define-syntax (define-type-class stx)

(define (parse-def name d)
(syntax-case d ()
[(generic (arg ...) body ...)
#`(!begin
#,(parse-def name #`(generic arg ...))
(define-default-method (generic arg ...) body ...))]
[(generic arg ...)
#`(define-generic (generic #,name arg ...))]))

(syntax-case stx ()
[(_ name generic-def ...)
(with-syntax ([def-name (format-id stx "define/~a" #'name)])
#`(!begin

(define name (make-parameter #f))

(define-syntax-rule (def-name instance (f a (... ...)) body (... ...))

(define (f a (... ...))
(using ([name instance])
body (... ...))))

#,@(map (λ (d) (parse-def #'name d))
(syntax->list #'(generic-def ...)))))]))

Roughly what it does is define generic methods that may have a default 
implementation provided just like in Haskell, as well as, in the 
"def-name" output, generate a macro to define functions that 
automatically parameterize the active class instance for a given type 
class (I did this to avoid dispatching on argument types since "mempty" 
takes no arguments. It seemed necessary to provide type information 
manually outside of the method calls.)

One potential band aid solution is to wrap the thing that applies generics:

(define-syntax-rule (define-generic (name type-class arg ...))
(define (name arg ...)
(apply-generic 'name (car (type-class)) arg ...)))

with a "!", so it'd be (! (apply-generic ...) ...)

But I'm not sure this is the proper solution or perhaps my definitions 
are somehow incorrect altogether.

On 07/01/2013 05:19 PM, Stephen Chang wrote:
> For #2, can you give a more specific example?
>
> For example, I can do this:
>
> #lang lazy
> (require (for-syntax racket/base))
>
> (cons 1 (cons 2 (cons 3 null)))
>
> (define-syntax econs
>    (syntax-rules () [(_ x y) (cons x (! y))]))
>
> (econs 1 (econs 2 (econs 3 null)))
>
>
> Welcome to DrRacket, version 5.3.4.11 [3m].
> Language: lazy; memory limit: 8192 MB.
> '(1 . #<promise>)
> '(1 2 3)
> On Mon, Jul 1, 2013 at 2:31 PM, Sean Kanaley <skanaley at gmail.com> wrote:
>> I'm attempting to provide a package very soon with Haskell-like type classes
>> initially including Monoid, Functor, and Foldable, however I've discovered
>> the terrible bug that mappend is too strict when it expands into "or" or
>> "and".
>>
>> Is there a way to extend eval from within #lang racket such that the user
>> can still use #lang racket while I secretly have an extended version?  I
>> would then add a lazy-lambda construct without requiring the entire set of
>> future racket programs written using this library to be lazy.  In other
>> words, if eval was a superclass or interface available in #lang racket,
>> someone calling (mappend ...) would get the closure of some sort to be
>> applied by this generic eval that I would implement internally to handle the
>> fact that it's not a regular lambda.  If not, then solution 2:
>>
>> #lazy ... simply using #lazy causes a couple issues:
>>
>> 1. define is not defined at phase1, solved by (require (for-syntax
>> racket/base) ...)
>>
>> 2. define-syntax not valid in expression context etc. ... not sure how to
>> solve ... it looks like #lazy doesn't permit internal definitions, but then
>> how does one make a macro in lazy racket?
>> ____________________
>>   Racket Users list:
>>   http://lists.racket-lang.org/users
>


Posted on the users mailing list.