Arc Forumnew | comments | leaders | submitlogin
2 points by akkartik 4845 days ago | link | parent

Thanks for catching that! I've been thinking about that bug: https://github.com/akkartik/wart/commit/90b6df8616a34635feeb.... I figured nobody would ever use aif without using it :)

Hmm, you lost me with delisting. Can you describe its semantics in english? What's that nil for in the call?

And why do we need g-rest? Why not just make it:

  (mac delisting (rest lst . body)
    `(withs (,rest ,lst)
       ,(whenlet (first . body) body
          (iflet (nextvar . body) body
            `(if (acons ,lst)
               (let ,nextvar (car ,lst)
                 (delisting ,rest (cdr ,lst)
                   ,@body))
               ,first)
            first))))
I don't see any change in behavior. I thought maybe you were trying to use lst in two different senses and wanted to be clear about that, but it's not that either..

In wart all args are optional (like javascript), so I could just:

  (mac aif(expr then . more-branches)
    (if (or then more-branches) ; <= modified
      `(let it ,expr
         (if it
            ,then
            (aif ,@more-branches)))
      expr))
Update: Fixed (https://github.com/akkartik/wart/commit/32401beaf45139ae0340...)


1 point by rocketnia 4845 days ago | link

Wouldn't that 'aif have (aif t nil) return t?

Hmm, you lost me with delisting.

It's a utility to cut down on nesting (iflet (a . b) ...) forms, a common pattern I use when giving a utiility different behavior depending on the number of arguments. The primary goal is to have the branches lined up on the right side while the local variables are distributed so they don't get in the way. Particularly, a variable is positioned just before the first branch it's bound in. The rest variable is an exception; it makes sense at every level, but I don't want it to be perceived as the first element of the list, so I've put it in a different place. (Note that it doesn't actually make sense everywhere unless the thing being destructured could be a dotted list or non-nil atom.)

The three branches in my 'aif's use of 'delisting are the zero-element branch (just nil), the one-element branch, and the two-or-more-element branch. The "two or more" thing might be tricky to perceive; maybe the utility should be limited to proper lists and the last case should be preceded by both its usual variable and the rest variable.

Meanwhile, the point of 'g-rest is to allow for destructuring. Destructuring an atom might be weird, but most of the time the rest isn't an atom, and nil can be destructured just fine.

-----

1 point by akkartik 4845 days ago | link

Ok, I'm starting to see. So the code in each branch binds all variables upto that point?

When I see:

  first    first
I'm inclined to read this as, "if you just see a variable (call it first) and nothing else after, just return first." I imagine the evaluation going to the next case and saying[1], "oops, nothing left, ok, go back and execute code for the previous case." Am I on the right track? Hmm, I've never seen anything like this.

And the nil is a branch with an empty 'guard'. Ahhhh.. this is cool :)

[1] Dijkstra hated anthropomorphizing code.

-----

2 points by rocketnia 4845 days ago | link

You're on the right track. ^_^ Like I said, 'delisting just exists to better organize a pattern I use all the time.

A visual example would probably work wonders. Here's the pattern:

  (iflet (<var1> . <rest>) body
    (iflet (<var2> . <rest>) <rest>
      (iflet (<var3> . <rest>) <rest>
        <three-or-more-arg case>
        <two-arg case>)
      <one-arg case>)
    <zero-arg case>)
And here's the same code using (this version of) 'delisting:

  (delisting <rest> body
            <zero-arg case>
    <var1>  <one-arg case>
    <var2>  <two-arg case>
    <var3>  <three-or-more-arg case>)
Any version of 'delisting should have basically the same elements. I'm just not sure where to put them.

Incidentally, halfway through writing that post with 'delisting and 'aif, the 'aif code read (decomposing body ...). XD

-----

1 point by akkartik 4845 days ago | link

Ok, I see the light. That explanation suits me perfectly :)

-----

1 point by rocketnia 4845 days ago | link

I just hope my code doesn't all devolve into "decomposing body" and "ifdecap." >< Talk about anthropomorphizing code.

-----

1 point by akkartik 4845 days ago | link

Hmm, (aif t nil) works fine for me. aif perhaps doesn't need that gensym even if delisting does in general.

-----

1 point by akkartik 4839 days ago | link

Ack, I misunderstood you. I've retreated to a version with car/cdr manipulation; looks like there's no way around it (it's still simpler than the original, but that just makes me wonder what bugs remain)

https://github.com/akkartik/wart/commit/9425c4829abb88052d29...

-----

1 point by rocketnia 4839 days ago | link

I haven't tested it, but I don't see any bugs from here.

I'm a bit surprised you broke up the (if branches ...) condition into two places like that, though. I think it would have been simpler just to replace "(or then more-branches)" with "branches". ^^

-----

1 point by akkartik 4839 days ago | link

:) Yeah I thought of that in the next CL.

-----

1 point by akkartik 4845 days ago | link

Incidentally, does this seem like a good idea?

  arc> (iflet a nil 34 35 a)
  Error: "reference to undefined identifier: _a"
I've modified wart to have iflet mimic aif closely.

  wart> (iflet a nil 34 35 a)
  35
In fact, aif is now directly implemented using iflet (https://github.com/akkartik/wart/commit/2880402dc5ae2d96be7f...). Does this introduce a bug I haven't considered?

-----

1 point by rocketnia 4845 days ago | link

I prefer having 'aif be a special case of 'iflet too. The change you've made would only come up every so often. It might break some code that uses 'iflet for destructuring and the tests some other condition if that fails (whose result can't be destructured), but that's easy enough to fix.

For Penknife I've been thinking about something a bit different:

  [itif x
    do-something-with.it
   ifdecap first rest lst
    [do-something-with first rest]
   iflet result foo.bar
    baz.result
   do do-something-else.]
In this in-progress draft, every if-like construct parses its elses using the same utility. The point is to let them work together better while making the indentation less controversial and confusing than it is in Arc. The downside so far is that every single unconditional else needs a 'do in front of it to look nice, which feels a bit like (cond ((#t ...))).

-----

1 point by akkartik 4845 days ago | link

"It might break some code that uses 'iflet for destructuring and the tests some other condition if that fails (whose result can't be destructured).."

Ah, destructured args!

https://github.com/akkartik/wart/commit/9cb777987467bedf4446...

Thanks for the bug report! :)

-----

1 point by akkartik 4845 days ago | link

ifdecap?

-----

1 point by rocketnia 4845 days ago | link

Penknife's version of (iflet (a . b) x ...) is [ifdecap a b x ...]. There's no destructuring yet, not to mention that Penknife has no notion of a dotted list.

I called 'ifdecap "ifpair" at one point, but I want it to be extensible, with a clear metaphor of first and rest rather than left and right.

-----

1 point by akkartik 4845 days ago | link

Ok, but why decap? Just a random pronounceable name for something low-level you didn't want to use a good name for?

-----

1 point by rocketnia 4845 days ago | link

Can you think of a better one? XD This isn't supposed to be that low-level. I'm working on the Penknife core from a top-down view right now, and I've considered having all argument lists decompose using ifdecap just so that a function can be applied to a user-defined type of list (which might support a user-defined kind of destructuring, for instance; think keyword args).

-----

1 point by akkartik 4845 days ago | link

I wasn't expressing dislike but curiosity. How did you end up with that name? Does it stand for 'decompose a pair'?

-----

1 point by rocketnia 4845 days ago | link

Lol. XD It's short for "if-decapitate."

Come to think of it, ifdecap is a bit paradoxical.... If it's used to destructure argument lists, then it'll be recursive upon itself over and over and probably never get anywhere. (It needs to return two values somehow, which means either the caller or a CPS-style callback will need to destructure those values. I don't plan to look for a multiple-value-return-esque solution.) I guess the built-in argument list type will need some kind of hardwired treatment just like the built-in function type.

-----

1 point by evanrmurphy 4845 days ago | link

> In wart all args are optional (like javascript)

How's this working out for you in general? Any disadvantages?

Does wart still have the `?` syntax for keyword args as in http://arclanguage.org/item?id=13083?

I guess I should try wart and see these things for myself. :)

-----

2 points by akkartik 4845 days ago | link

:) This is a pretty good exhibition of features: https://github.com/akkartik/wart/blob/master/023conditionals.... You can see that the '? for optional' syntax hasn't changed, that $vars and o$vars are convenient and make several macros shorter. I suppose the fact of all args being optional is a little subtle and needs context to read between the lines.

But now I've let you put off trying wart again :) At some point I want to put a little webapp together to make reading wart code more convenient. So you can click 'next' from boot.lisp to go to 000.lisp (next in load sequence), tests are just a click away, etc. Really my only goal is to make it a pleasure to read, and I'd give an arm to hear experiences of lisp programmers reading it.

I haven't had any trouble with optional args (though this is the first time I've noticed them being useful). Or breaking constraints in general. I suspect the problem is not constraints I've broken but constraints I'm not even aware of.

-----