Arc Forumnew | comments | leaders | submitlogin
More generic if macro
2 points by byronsalty 6122 days ago | 3 comments
After writing some code which looked like:

  (if (no (expr1)) 
      (...)
   
I was thinking "there must be a macro already like 'ifn' which would take out the extra (no ...)". Unless works sort of like this but it's more like "when" than "if".

Unless doesn't work for the following, what if I wanted the equivalent of:

  (if (no (expr))
       (...then...)
       (...else...))
That's not supported.

Also if I wanted multiple (if (nos)) like:

  (if (no (expr1))
       (...)
       (no (expr2))
       (...))
The code to write a "ifn" macro is so similar to aif:

  (mac ifn0 (expr . body)
    `(if (no ,expr)
       ,@(if (cddr body)
             `(,(car body) (ifn0 ,@(cdr body)))
             body)))
Here's the current aif:

  (mac aif (expr . body)
    `(let it ,expr
       (if it
           ,@(if (cddr body)
                 `(,(car body) (aif ,@(cdr body)))
                 body))))
They are very similar - so much so it's a smell. The only difference is that, one applies the "no" function, one captures the "it" variable, and then when recursing they call different macros.

So here's a more generic if macro which pulls out the similarities:

  (mac genif (sym f expr . body)
    `(let ,sym (,f ,expr)
       (if ,sym
         ,@(if (cddr body)
               `(,(car body) (genif ,sym ,f ,@(cdr body)))
               body))))
Now we can express both aif and ifn like:

  (mac ifn (expr . body)
    `(genif ,(uniq) no ,expr ,@body))

  (mac aif (expr . body)
    `(genif it idfn ,expr ,@body))

  ; create a new macro mixing the two above
  (mac aifn (expr . body)
     `(genif it no ,expr ,@body))
As a bonus - I believe iflet can be re-written using genif. I'm not sure because I don't see how to test it but based on what the code is doing the following is more or less equivalent:

  (mac iflet (var expr then . rest)
    `(genif ,var idfn ,expr ,@(cons then rest)))
This is not 100% equivalent because genif will make the captured var available in both the "then" and "else" clauses. It will also reset the value of var on subsequent "else-if" clauses. The arc1 iflet doesn't appear to do either of those which I'd argue are bugs because they are inconsistent with aif.


6 points by bogomipz 6122 days ago | link

You could use this, though;

  (if (~expr1)
        (...)
      (~expr2)
        (...))

-----

1 point by raymyers 6122 days ago | link

True, though that only works for function calls -- not variables: (if (no li) ...)

-----

1 point by byronsalty 6122 days ago | link

Well leaving ifn out of the picture, I did a codetree on using genif to replace aif and iflet and unfortunately the size increased from 7759 to 7777.

Of course if iflet really is buggy then the code tree would likely increase to fix the bugs.

-----