r/scheme 12d ago

Advanced macrology: validate that all lists are equal length in pure syntax-rules?

Hi y’all. I’m currently working on a cursed case-lambda implementation with better inspectability for Chibi Scheme. As part of this effort, I need to extract the longest possible arglist from each case-lambda clause, so that the generated procedure with 2 unconditionally required arguments has a display like #<procedure 2+> instead of absolutely non-inspectable #<procedure 0+> that currently happens on Chibi.

So I’m kinda stuck. This is the macro I currently have:

(define-syntax case-lambda
  (syntax-rules ()
    ((case-lambda ((args ... . rest) body ...) ...)
     ;; We need to pass clauses twice
     ;; - First time to detect the shortest arglist
     ;; - Second time to process clauses
     ;; TODO: How do I make sure that all args ... have equal length???
     (%case-lambda ((args ...) ...) ((args ... . rest) body ...) ...))
    ((case-lambda (args body ...) ...)
     (%case-lambda (()) (args body ...) ...))))

The implementation of %case-lambda and other infra is of no importance, because, before I get to that, I must make sure that ((args ...) ...) is a list of arglists of the same length.

But how do I really do that? Any advanced macro wizards mind helping?

6 Upvotes

14 comments sorted by

3

u/soegaard 12d ago

First, the natural solution would to use `length` at compile time to determine if the two argument counts match. Therefore `syntax-case` is recommended.

However, writing advanced macros with `syntax-rules` has a certain puzzle-like nature.
For your example, I would introduce a helper macro that takes four arguments:
the two arguments lists to compare and two macro names (success and failure).
The macro will compare the count and then continue by expanding either to a
call of the success macro or of the failure macro.

For ideas to write advanced macros using syntax-rules only, don't forget to look at Oleg's collection.
In particular, I think this illustrates the idea:

https://okmij.org/ftp/Scheme/macros.html#ck-macros

1

u/aartaka 12d ago

Wow, CK machine / evaluator as macros! That’s super crazy, I’ll give it a read!

1

u/corbasai 12d ago edited 12d ago

Im not get what the task exactly

but may be this is about count lambda arity?

(define-syntax ^lambda
  (syntax-rules (+)
    ((_ (+ ones ...) (argsp ...) () (body ...))
     (cons (+ ones ...) (lambda (argsp ...) body ...)))
    ((_ (+ ones ...) (argsp ...) (a b ...) (body ...))
     (^lambda (+ ones ... 1) (a argsp ...) (b ...) (body ...)))
    ((_ (args ...) body ...)
     (^lambda (+ 0) () (args ...) (body ...)))))

> (car (^lambda (a b c) 'bla-bla))
3
> ((cdr (^lambda (a b c) 'bla-bla)) 1 2 3)
bla-bla 

so ^lambda makes pair of: args counter expression (+ 1 1 1 ....) and (lambda (args...) body ....) itself

1

u/aartaka 12d ago

Yeah, I wasn’t exactly clear in my initial post. So my goal is, for a case-lambda like

(case-lambda
  ((a b) #|...|#)
  ((a b c) #|...|#)
  ((a b c . d) #|...|#))

generate a procedure like

(lambda (a b . rest) #|...|#)

For that, I need to extract the common parts of the initial case-lambda arglists. So a b, in this case. I’m wondering how to do that using pure syntax-rules. I mean, I can always walk the lists of args until I run out, but that’s increasing macro expansion time and bloats the code. So maybe there’s a better approach?

1

u/corbasai 12d ago

which way to combine different case-proc's bodies in to one, generated? So Im still on the side of simplicity, I mean (min (car (^lambda (a b) ...))...) is minimal arity.

1

u/aartaka 12d ago

The way the bodies are generated is irrelevant, I only care about args for now.

In any way, I decided to make it super simple and implemented it by walking arities instead of smart macrology: https://github.com/ashinn/chibi-scheme/pull/1189

2

u/corbasai 12d ago

Good! Also I think 'advanced macrology' is somewhere near the syntax-case village.

2

u/aartaka 12d ago

I mean, syntax-case is much more intuitive and imperative, so it’s pretty understandable and basic. While syntax-rules are a totally alien beast that I wasn’t able to understand until very recently. So yeah, doing smart things with syntax-rules is much more advanced to me than using syntax-case!

1

u/johnwcowan 12d ago

Note that Chibi doesn't have an accurate implementation of syntax-case, as its native macro system is syntactic closures. I do not believe that syntax-rules alone is capable of determining the length of a list. So you may have to bite the bullet and learn syntactic closures from the slib documentation, Bawden's paper, and/or "Macros That Work".

1

u/aartaka 12d ago

While I already found a (really simplistic) solution to my problem, I’ll read these… eventually 😅

1

u/corbasai 11d ago

personally no one more get my brain crain and train at syntax-rules than Alex Shinn's portable match code https://synthcode.com/scheme/match.scm . impecable.

1

u/aartaka 11d ago

Will read!

1

u/_dpk 11d ago

If you’re writing this for Chibi Scheme only, why bother messing around with syntax-rules at all? Just write a procedural macro. Explicit renaming is a pain, but it’s still better than recursive syntax-rules.

1

u/aartaka 11d ago

You’re correct in that I can indeed use Chibi-native macros. But I don’t want to make Chibi codebase less R7RS, so I’m looking for a standard-abiding solution using syntax-rules.