| PG: "Most of the operators in Rtml were designed to take keyword parameters, and what a help that turned out to be. If I wanted to add another dimension to the behavior of one of the operators, I could just add a new keyword parameter, and everyone's existing templates would continue to work. A few of the Rtml operators didn't take keyword parameters, because I didn't think I'd ever need to change them, and almost every one I ended up kicking myself about later. If I could go back and start over from scratch, one of the things I'd change would be that I'd make every Rtml operator take keyword parameters." (http://lib.store.yahoo.net/lib/paulgraham/bbnexcerpts.txt) Rediscovering this quote, one realizes how curious it is that arc doesn't have keyword parameters. You can fudge them using the optional-alist trick (http://arclanguage.com/item?id=629), but that's a hack. For example, it doesn't work in macros where I also want to pass in a body parameter to inline code. Like if I want to add optional keyword parameters to influence rendering in this pagination macro: (mac paginate(url numitems . body)
`(withs (start-index (param req "from")
end-index (+ start-index ,numitems))
,@body
(nav ,url start-index end-index)))
My current syntax: a :do separator in calls to this macro: (paginate '/search' 10
;; optional keyword args go here
nextcopy "Next →" prevcopy "← Prev"
:do
(each doc (cut docs start-index end-index)
(render-doc doc)))
I figure the keyword :do is more readable than an extra set of parens around the optionals or the body.Here's how I have to rewrite paginate: (mac paginate(url numitems . block)
(let (params body) (kwargs block '(nextcopy "next" prevcopy "prev")) ; <-- defaults for keyword args
`(withs (start-index (param req "from")
end-index (+ start-index ,numitems))
,@body
(nav ,url start-index end-index ',params))))
Auxiliary definitions: (def list-len(l)
(if (acons l)
(len l)
0))
(def alist? (l)
(if (isa l 'cons)
(all 2 (map list-len l))))
(def coerce-tab(tab)
(if
(isa tab 'table) tab
(alist? tab) (listtab tab)
(isa tab 'cons) (listtab:tuples tab)
(table)))
(def merge-tables tables
(let ans (table)
(each tab tables
(maptable (fn(k v) (= ans.k v)) coerce-tab.tab))
ans))
(def regroup args
(let do-idx (pos ':do args)
(if (and args do-idx)
(withs (kwtbl (tuples:cut args 0 do-idx)
body (cdr:cut args do-idx))
(list kwtbl body))
(list args))))
(def kwargs(args-and-body defaults)
(let (params body) (apply regroup args-and-body)
(list (merge-tables defaults params) body)))
Question: What do people think of the :do syntax? Can you think of a good syntax to add python-style keyword parameters without compromising rest parameters? |