Actually on closer examination the bug was subtler than that. I did check whether the incoming request was allowed to modify each field it was trying to modify. But the code that decided whether a field was modifiable was using a four variable list to destructure on a five value description of the field. This caused the variable determining whether a field was modifiable to be bound instead to the value saying whether or not it should be displayed. Which meant every field that was displayed was modifiable.
That's actually pretty interesting to me. I didn't notice that when I was looking at the Hacker News code. Perhaps Arc shouldn't allow a list to be destructured except with a list of the same length? Allowing differing lengths seems likely to cause subtle bugs like this.
I generally tried to err on the side of flexibility, and it seemed more flexible not to require the lengths to match. I could imagine cases where you might want to use patterns both longer and shorter than the lists they were matched against. On the other hand, I'm not sure I've ever ended up taking advantage of this.
It sounds like you're treating list destructuring like CL's mvbind. While it's subjective of course, I'm more comfortable with the runtime doing what I mean when I control the number of values (modulo `(apply values ...)').
I'm not comfortable with destructuring values "flexibly" when I don't control the data coming in.
Except "_" is a perfectly good name for a symbol in Arc.
"nil" could be used instead, since nil isn't allowed to be rebound.
(let (x . nil) '(a b c) x)
even works already, since (unless it's been changed in some newer version of Arc I haven't installed yet) Arc just silently ignores the let-ing of nil:
But at the same time this junk variable is code-as-documentation professing the incomplete destructuring is intentional.
You could go a step further and have a special ignore symbol, (for instance * ) like most pattern matching languages/libraries have with the property that it can appear multiple times:
In a strongly typed language one would normally want to reify the number in the type system whenever possible, i.e. the use of (a,b,c,d,e) rather than [a,b,c,d,e] in Haskell (that's a 5-tuple rather than a 5 element list).
I think that people used to dynamic programming languages would normally pick the list rather than the tuple simply because it at first appears easier. I think Dijkstra had something to say on that particular subject :)
Agreed. But it sounded like, in this case, you were using a list to represent that which would have been represented by a record in a statically typed language.
I'm imagining you'd have some kind of Field record type, with isDisplayable and isModifyable fields. Instead of destructuring as a list (or tuple), you would explicitly look for isDisplayable and isModifyable.
But, of course, you could still make the same mistake in a statically typed language if you chose not to set up the data structure this way.