function argument converter

I notice I have a lot of code where I want to make sure I have my function arguments converted into something:

(def foo (x)
  (let x (string x)
    (+ x 456 x)))

conanite posted a cool solution: since it doesn’t make sense to have an argument variable name itself contain a colon (it couldn’t be referenced without triggering Arc’s compose syntax), having a colon in a function argument could be used for something. Such as... calling a conversion function.

(def foo (x:string)
  (+ x 456 x))

(def mem (f:testify seq)
  (reclist [if (f:car _) _] seq))

(It was late at night and I had forgotten about conanite’s implementation and wrote my own, you’ll notice that I have the variable first while conanite has it the other way around).

Particularly pleasing to me is that this is the opposite of a strict type declaration: in many languages, “x:string” would mean that x had to be a string; instead we’re saying that we’re going to make x be a string.

arc> (foo 123)
arc> (mem 3 '(1 2 3 4 5 6))
(3 4 5 6)

Here’s my quick and dirty implementation. This doesn’t handle optional arguments, argument list destructuring, converting rest arguments, anonymous fn functions, or other macros that generate anonymous functions such as let or with... but it’s enough to play with :-)

; (var-convert 'a:b) => (a b)

(def var-convert (x)
  (and (isa x 'sym)
       (find #\: (string x))
       (map sym (tokens (string x) #\:))))

; (undot '(a b . c)) => ((a b) c)

(def undot (lst)
  (xloop (xs lst r nil)
    (if (no xs)
         (list (rev r) nil)
        (atom xs)
         (list (rev r) xs)
         (next (cdr xs) (cons (car xs) r)))))

; (dot '(a b) 'c) => (a b . c)
; iirc there was a fix to get join to do this; I'd have to go look for it

(def dot (lst dotted)
  (if (no lst)
       (dot (butlast lst) (cons (last lst) dotted))))

(= original-def def)

(mac def (name args . body)
  (or (if (alist args)
           (let (args rest) (undot args)
             (if (some var-convert args)
               `(original-def ,name ,(dot (map [iflet (var convert) (var-convert _) var _] args) rest)
                  (with (,@(mappend [iflet (var convert) (var-convert _)
                                            `(,var (,convert ,var))]
      `(original-def ,name ,args ,@body)))

Get this hack

Using the hackinator:

$ hack \ \ \ \

Or, since for once no patches are involved, download and load:

$ wget
$ mzscheme -f as.scm
Use (quit) to quit, (tl) to return here after an interrupt.
arc> (map load '("butlast0.arc" "xloop0.arc" "fn-arg-converter0.arc"))
*** redefining def
(nil nil nil)


This code is in the public domain.

Contact me

Twitter: awwx
Email: andrew.wilcox [at]