Arc Forumnew | comments | leaders | submitlogin
1 point by Pauan 4957 days ago | link | parent

"There's two separate issues here: whether we need del in addition to '=, and whether places should support slices and steps. Let's discuss them separately."

Of course. I don't recall ever conflating the two, and I think we should allow for both assigning to slices, and deleting slices. In fact, then deletion could use assignment, so this:

  (del (foo 0 1))
Would expand to this:

  (= (foo 0 1) nil)


1 point by akkartik 4957 days ago | link

  (= (foo 0 1) nil)
Incidentally, I just remembered that there's already a name for assigning to nil: wipe. May still make sense to have both names, though.

-----

1 point by Pauan 4957 days ago | link

Yes, except we would need to change sref so it understands slices. Which I think we should do.

-----

2 points by rocketnia 4957 days ago | link

I don't. We already have (= (foo 0) ...) modifying the first element, so we don't have room for it to modify the slice 0..<(0 + 1). We already have (zap inc (my-table idx 0)) incrementing my-table.idx with a default starting value of 0, so we don't have room for it to modify the slice idx..<(idx + 0).

(FYI: The ..< I'm using is Groovy's notation for half-inclusive ranges, inspired by Ruby's ... notation but less ambiguous with Groovy's other syntax. I also prefer it for its obvious asymmetry.)

It would probably make more sense to use (slice foo ...) to get a slice and (= (slice foo ...) ...) to put it back.

This isn't ideal, as far as I'm concerned, 'cause it means we don't always get what we put in:

  arc> (= foo '(a b c d e f g))
  (a b c d e f g)
  arc> (= (slice foo 1 3) '(x y))
  (x y)
  arc> foo
  (a x y e f g)
  arc> (slice foo 1 3)
  (x y e)
However, that's not really part of the existing contract:

  arc> (= foo (table))
  #hash()
  arc> (= (foo 'a 1) nil)
  nil
  arc> foo
  #hash()
  arc> (foo 'a 1)
  1
Furthermore, I don't know a place I'd use that contract even if it did exist. ^_^ So I retract my objections for now, just as long as slice-getting isn't ambiguous.

-----

1 point by Pauan 4957 days ago | link

How would it be ambiguous? (foo 0 1) returns a list of the first element. (foo 0 nil) returns the entire list, and those would work in assignment too:

  (= (foo 0 1)   '(3 4))   ; splice a list into the first element
  (= (foo 0 nil) '(1 2 3)) ; replace the list with a different one
Hm... why not use cut rather than slice?

  (= (cut foo 0 1) '(3 4))
I mean, cut already exists, so...

-----

2 points by rocketnia 4957 days ago | link

"(foo 0 1) returns a list of the first element."

In tables, (foo 0 1) already means the same thing as (or foo.0 1). But hmm, maybe you're not interested in slicing tables. That's fine, then. ^_^

By the way, newLISP does something like that with (0 1 foo): http://www.newlisp.org/downloads/newlisp_manual.html#implici...

The other issue was that I think at some point you were using (foo 0) to mean a one-element slice, and that would definitely be ambiguous with regular old indexing.

---

"Hm... why not use cut rather than slice?"

I was going to say that, but I didn't want to clutter my point too much. Arc's 'cut accepts start and stop positions, and your slicing examples instead use a start position and a length. Translating between those was making my post harder to follow (I think), so I just assumed the definition of 'slice instead. Either should work. ^_^

-----

1 point by Pauan 4957 days ago | link

"In tables, (foo 0 1) already means the same thing as (or foo.0 1). But hmm, maybe you're not interested in slicing tables. That's fine, then. ^_^"

What would a table slice even mean? They're not even ordered. The closest thing would be a table that has a subset of the keys of another table, but that wouldn't use numeric indexing.

---

"The other issue was that I think at some point you were using (foo 0) to mean a one-element slice, and that would definitely be ambiguous with regular old indexing."

I was? (foo 0) should always be an element, not a slice. Ah well, it doesn't matter. If I did use that, I was incorrect.

I'll note that (del (foo 0)) is equivalent to (= (foo 0 1) nil) so perhaps that confused you.

---

"Arc's 'cut accepts start and stop positions, and your slicing examples instead use a start position and a length."

They did? I always thought of it as being a start/end position, like cut. So for instance:

  (= foo '(1 2 3 4 5 6))

  (= (foo 2 5) nil)

  foo -> (1 2 6)
The way I'm doing it is, the number of elements is the end minus the start, so for instance, in the example above, 5 - 2 is 3, so it removed 3 elements, starting at position 2. This is exactly how cut behaves:

  (= foo '(1 2 3 4 5 6))

  (cut foo 2 5) -> (3 4 5)
In fact, I think it'd be a good idea for lists/strings in functional position to just delegate to cut, so (foo 2 5) would be the same as (cut foo 2 5)

-----

2 points by rocketnia 4957 days ago | link

"What would a table slice even mean ?"

Hence my "but hmm." ^_^

---

"I'll note that (del (foo 0)) is equivalent to (= (foo 0 1) nil) so perhaps that confused you."

Yeah, that's probably it.

---

"I always thought of it as being a start/end position, like cut."

It's hard to tell because you use 0 as the start pretty often, but here are the two examples I was going off of:

From http://arclanguage.org/item?id=14656:

  (= foo '(1 2 3 4 5 6 7))

  (del (foo 1 4)) -> (1 6 7)
From http://arclanguage.org/item?id=14660:

  (= foo '(1 2 3 4))

  (del (foo 1 2)) -> (1 4)
Guess those were buggy. Well, now I know. :-p

-----

1 point by Pauan 4957 days ago | link

"Guess those were buggy. Well, now I know. :-p"

Ah, yeah, I switched from inclusive to half-open. And since I just so happened to use 1 as the start index, you couldn't really tell.

-----

1 point by akkartik 4957 days ago | link

What would a table slice even mean?

A sub-table with just the given keys?

-----

1 point by Pauan 4957 days ago | link

Well, sure, but you wouldn't use numeric indices to refer to that. :P It might be something like this:

  (subset x 'foo 'bar 'qux)
As opposed to:

  (x 0 1)
Which would be slice notation, which would work only on ordered sequences, like lists.

Which isn't to say that table subsets would be a bad idea, just that it's a different idea from list slicing, so there shouldn't be any ambiguities with slice notation (which was one of rocketnia's concerns).

-----

1 point by akkartik 4957 days ago | link

You could implement subsetting at function position, in which case it would be ambiguous. I already use :default in wart, so I don't care too much about that :) But yeah I hadn't thought of how to create ranges of keys. This may not be a good idea. You can't distinguish table lookup from a subset table with just one key/val, for example.

-----

1 point by Pauan 4957 days ago | link

Well, we could define it so table slicing would only happen with 3 or more arguments:

  (foo 'a 'b 'c)
...but I would consider that to be confusing, and would suggest an explicit function like "slice" or "subset".

-----

1 point by akkartik 4957 days ago | link

Yeah that's my sole feedback on del at this point: put the smarts into sref and defcall.

I have no idea how to do that :)

-----

1 point by Pauan 4957 days ago | link

Well then, guess I'll go in and do it, as a library for starters, and then we can potentially get it into base ar later.

By the way, sref has a terrible argument signature:

  (sref foo value key)
Intuitively, it really should be[1]:

  (sref foo key value)
But that's a backwards-incompatible change... Also, setforms only sends a single argument to sref, so we would need to change that too. For instance:

  (= (foo 0 1) 'bar)
Is translated into:

  (sref foo 'bar 0)
But it should be:

  (sref foo 'bar 0 1)
But if I change that, it breaks other things. Ugh. This really wasn't designed with slices in mind.

---

* [1]: To be fair, having the key as the last argument is great for slices, but bad for pretty much everything else. The argument order would make a lot more sense if sref had been designed with slice support.

-----

1 point by rocketnia 4957 days ago | link

"Also, setforms only sends a single argument to sref, so we would need to change that too."

Yeah, I think I noticed that at one point a long time ago, and I consider it to be a bug. (I promptly forgot about it. XD;; ) I never rely on that behavior, but then I don't extend things in the Arc core much either, for cross-implementation compatibility's sake.

---

"To be fair, having the key as the last argument is great for slices"

I think that's the reason it is the way it is. I've ended up designing some of my utilities the same way. For instance, I usually give failcall the signature (func fail . args), which is very similar to sref's (func new-value . args). I'd probably call 'sref "setcall" if I were to reinvent it.

I'm not sure I want to reinvent 'sref though: It's kinda bad as a failcallable rulebook, because you can't tell if it will succeed until you've called it, and then you get side effects. I've been thinking about other designs, like a currying-style ((sref func args...) new-value) version or something. But this is kinda specific to a system that uses failcall. In Arc, we can't really recover when an extension doesn't exist, so we don't write code that tries yet.

-----

1 point by Pauan 4957 days ago | link

Oh! I just found a bug! In arc.arc:

  (= (sig 'msec nil))
Should be:

  (= (sig 'msec))
Right? And here too...

  (= (sig 'seconds nil))
Should be:

  (= (sig 'seconds))
Okay, this is too much of a coincidence. aw, is there a reason for this, or was it just a mistake that we can safely fix?

-----