Open sorawee opened 3 years ago
Great idea! I really like how small the macro is.
I wonder if we can think of a clearer name. I like that pyret-for
gives credit to Pyret and gives a hint about one good way to use this macro (for-style loops). But it doesn't say much about what's really going on ... and if we understood that, maybe we'd find other ways to use this macro.
polydot-apply
?
map-comprehension
?
I'm not sure what "polydot" means.
comprehension
could work, I think. I would avoid map-
, since it supports more than map
.
Oh, "polydot" is the local name for the polymorphic dotted (...
) types that work for typing map
fold
etc. [1]. Typed Racket uses that name internally [link]. I don't know where else to find it in print.
Anyway, I was thinking that polydot-typed functions have something in common that makes them fit well with pyret-for
.
But looking back at your original message, I see that filter
also works well with pyret-for
. So I guess "polydot" is too specific.
[1] Practical Variable Arity Polymorphism. ESOP 2009
Today I explained this pyret-for
macro to a few people at Brown. While thinking about it, we came up with one way to generalize:
(apply/lambda f (arg-spec ...) body ...)
where arg-spec is one of:
- #:hole
- #:from [id expr]
- expr
and the sequence (arg-spec ....) has exactly one #:hole
expands to:
(let ((g (lambda (id ...) body ...)))
(apply f (convert/g arg-spec) ...))
where convert/g:
- replaces #:hole with g
- replaces #:from [id expr] with expr
- and leaves other expr alone
I like #:hole
a lot. That'd help this macro work with foldl
.
The other (non-#:from
) arguments would be inputs to f
that don't get passed along to the newly-built lambda.
Like this?
#lang racket
(require syntax/parse/define
(for-syntax racket/match
racket/list))
(begin-for-syntax
(define-syntax-class arg-spec
#:attributes ([tmp-id 1] gen-match-code expr)
(pattern #:proc
#:with (tmp-id ...) #'()
#:with gen-match-code #'(begin)
#:with expr #'the-function)
(pattern [pat:expr #:from expr:expr]
#:with (tmp-id ...) (generate-temporaries (list #'id))
#:with gen-match-code #'(begin (match-define pat tmp-id) ...))
(pattern expr:expr
#:with (tmp-id ...) #'()
#:with gen-match-code #'(begin)))
(define (validate-proc-once args)
(define proc-list
(filter (λ (arg) (equal? '#:proc (syntax-e arg))) (syntax->list args)))
(match (length proc-list)
[1 #f]
[0 args]
[_ (first proc-list)])))
(define-syntax-parse-rule (apply/λ f:expr {~and args (arg:arg-spec ...+)} body:expr ...+)
#:fail-when (validate-proc-once #'args) "#:proc must appear exactly once"
(let ([the-function (λ ({~@ arg.tmp-id ...} ...)
arg.gen-match-code ...
body ...)])
(f arg.expr ...)))
(apply/λ andmap (#:proc [(list x) #:from '((1) (2) (3))] [y #:from '(3 2 1)])
(even? (+ x y)))
(define (hof offset xs proc)
(+ offset (apply + (map proc xs))))
(apply/λ hof (100 [x #:from '(1 2 3)] #:proc)
(* 2 x))
foldl
and foldr
unfortunately still couldn't be expressed with this generalization.
Ah! I didn't mean to make you implement it --- I thought we were just talking about possibilities.
That's awful about fold. I guess I'd want #:proc
to take an (optional?) arg to give the order of parameters, but that screws up the match-define ....
;; ... something like this
(apply/λ foldl (#:proc (x* y acc) [acc #:from 0] [(list x) #:id x* #:from '((1) (2) (3))] [y #:from '(3 2 1)])
(+ x y acc))
😞
Actually, my version above also has a bug. I should check 'paren-shape
for [<x> #:from <y>]
. Otherwise, a function application (some-function #:from 123)
will be parsed as a apply/λ
binding.
Many common higher-order functions consume a function value as the first argument, and
n
more arguments after that, where the function value acceptsn
arguments, which corresponds to the arguments in the call in some way. Examples include:map
,filter
(only one argument),andmap
,ormap
(foldl
andfoldr
have arguments in a wrong order, so they don't quite work)Example code without
pyret-for
The problem is that
Example code with
pyret-for
The
pyret-for
syntax, based on Pyret'sfor
, can be used to invoke this kind of higher-order functions.pyret-for
additionally improves upon Pyret'sfor
by allowing arbitrarymatch
pattern.Macro
Licence
MIT License / CC BY 4.0