| I recently wrote a Tcl proc, iter (http://wiki.tcl.tk/47678), that allows the programmer some control over iteration inside the loop body. It exposes a next command that returns the next element in the list being looped over. next also jumps the iterating variable ahead one. As explained on the tcl wiki page, I am finding iter useful for parsing arguments to a command. In other languages named arguments that can be passed to a command out of place are known as keyword arguments. I realized Arc does not have built in keyword arguments, and figured it would be interesting to see how iter could be written in Arc. What follows is an implementation of keyword parameters in Arc, as well as argument parsing notation analogous to what I am using in Tcl. Main funcs/macs to check out below are ndef and argparse, both of which are supported by iter. Some comments to start a discussion: My keyword arguments have to be passed as quoted symbols, like this (f 'key1 val1 'key2 val2). In Tcl we can just pass words around like this [f key1 $val1 key2 $val2]. I know Arc's typing won't allow us to use plain words like that, but it would be nice to have something other than quotes. Colons maybe? @akkartik, I see your keyword args use colons. I'll check that out and see if I can merge the idea into my system. (mac iter (var list . body)
(let i (uniq)
`(if (is nil ,list)
nil
(withs (,i 0
,var (,list ,i)
next (fn ()
(++ ,i)
(= ,var (,list ,i))))
(while (< ,i (len ,list))
,@body
(next))))))
(def 1+ (n)
(+ 1 n))
(def default-kwvals (argnames)
(mappend (fn (name) (if (atom name)
`(,name nil)
`(,(car name) ,@(cdr name))))
argnames))
;; Set up a context w/ positional arguments assigned to the correct values
(mac w/posargs (argnames argsvar . body)
`(with ,(intersperse nil argnames)
(= ,@(mappend (fn (name idx) `(,name (,argsvar ,idx)))
argnames (range 0 (len argnames))))
,@body))
;; Set up a context w/ keyword arguments assigned to the correct values
(mac w/kwargs (kwargs argsvar . body)
(with (arg (uniq)
justnames (map (fn (name) (if (atom name)
name
(car name)))
kwargs))
`(with ,(default-kwvals kwargs)
(iter ,arg ,argsvar
(case ,arg
,@(mappend (fn (a) `(,a (= ,a (next)))) justnames)))
,@body)))
;; 'named def' wraps the function body in a context that handles keyword arguments.
;; keyword args can have default values if they are given as a list. the car of
;; the list will serve as the name, the cdr as the default val.
(mac ndef (name _args . body)
(withs (kwstart (pos '--keys _args)
pargs (firstn kwstart _args)
kwargs (nthcdr (1+ kwstart) _args))
`(def ,name args
(w/posargs ,pargs args
(w/kwargs ,kwargs args
,@body)))))
(ndef fullname (msg --keys (first "Abe") (last "Lincoln"))
(prn first " " last " says " msg))
(fullname "four score and...")
;; Abe Lincoln says four score and..."
(fullname "hey, you sass that hoopy frood...?" 'last "Prefect" 'first "Ford")
;; => Ford Prefect says hey, you sass that hoopy frood...?
(mac argparse (args . case-body)
(let arg (uniq)
`(iter ,arg ,args
(case ,arg
,@case-body))))
(def scrape (addr . args)
(with (outfile ((tokens addr #\/) -1)
use-ssl nil)
(argparse args
-o (= outfile (next))
-ssl (= use-ssl t))
(prn outfile)
(prn use-ssl)))
(scrape "www.foo.com/index.html" '-o "foo.html" '-ssl)
;; => foo.html
;; t
|