Arc Forumnew | comments | leaders | submit | zachbeane's commentslogin
6 points by zachbeane 6173 days ago | link | parent | on: Is Arc good for big projects?

If Viaweb is anything like the code from Arc, ANSI Common Lisp, or On Lisp, it's hardly the usual Lisp style.

-----

7 points by pg 6172 days ago | link

I meant programming style in the sense of using exploratory programming techniques, not in the sense of using particular textual conventions or language features.

I think you're also confusing Lisp with Common Lisp. In a lot of ways (the long names, CLOS, the loop macro) CL is kind of unLispy. The coding style used in those two books (and Viaweb) is closer to the conceptual core of Lisp.

-----

2 points by zachbeane 6174 days ago | link | parent | on: Array, Vector ?

Does that mean that Arc must be unsuitable for implementing itself? Why?

-----

2 points by zachbeane 6176 days ago | link | parent | on: Clarification about Character Sets

The inability to memoize functions that might return nil is annoying. Memoizing is important.

-----

1 point by randallsquared 6176 days ago | link

So:

    (def memo (f)
      (let cache (table)
        (fn args
          (aif (cache args)
              (cadr it)
              (cadr (= (cache args) (list t (apply f args))))))))

-----

2 points by zachbeane 6176 days ago | link

A less gross solution is available if you can ask if a key is present in a table.

-----

1 point by randallsquared 6176 days ago | link

We differ on whether that would be less gross. :)

-----

2 points by zachbeane 6176 days ago | link

If there was an "intable" function, you could do it in 11 forms (20 symbols), but your solution requires 13 forms, or 22 symbols. The shorter solution would be less gross, by definition!

  (def memo (f)
    (let cache (table)
      (fn args
	(if (intable cache args)
	    (table args)
	    (= (table args) (apply f args))))))

-----

1 point by randallsquared 6176 days ago | link

If intable could be made to return the actual value in the table, I'd have no reservations, since you could

    (aif (intable cache args)
         (thevalue it) ; where the fn thevalue does cadr
                       ; or whatever it needs to do
         (= (table args) (apply f args)))
Scanning over the entire table twice, once to see if the key is there, and once to actually get the value, is what seemed gross to me about checking for the key first. No doubt that could be optimized by having tables keep a list of keys separately, but that seemed like a heavyweight fix.

I agree that your version is prettier on the surface, though.

-----

12 points by zachbeane 6176 days ago | link | parent | on: Clarification about Character Sets

Arc's version of CL's (incf (gethash k table 0)) is gross.

-----

5 points by randallsquared 6176 days ago | link

Seems like it wouldn't be hard to add defaults to the hash referencing (and string referencing), to allow (++ (table k 0)) which seems much nicer than the CL version, in my opinion.

Edit: Actually, not string and array referencing, since defaults don't matter for that, and there's a much more useful meaning for that: slices.

-----

6 points by pg 6176 days ago | link

I might do it that way, but it seems cleaner to do it when hash tables are created.

-----

5 points by randallsquared 6176 days ago | link

Only if every key is a similar type. The default that's most appropriate is often peculiar to how you're using the value or location at the use site.

-----

2 points by lg 6176 days ago | link

speaking of hash tables, I remember in ACL you explained why CL has two return values for gethash, to differentiate between the nil meaning "X is stored as nil in my table" and the nil meaning "X is not stored in my table". So why not in Arc?

-----

2 points by pg 6175 days ago | link

Because it turns out that in practice it's rarely to never an issue. If you have nil as a val in a hash table, you usually don't care whether that's because it was never set, or explicitly set to nil.

-----

1 point by dr_drake 6171 days ago | link

Dear Paul, I can not believe you would make such a statement. Either you're living in a vastly different programming universe than the one I am living in, or you really haven't done that much programming at all. In any case, there are many situations where one stores types of values that may include nil in a hash table, and in most of these there is a very significant difference between 'value is nil' and 'value is not stored'. I understand that Arc isn't trying to all 'enterprisey', but these are fundamental concepts that, I thought, only complete amateurs did not understand. Sincerely, Dr. Drake

-----

2 points by pg 6171 days ago | link

You know, I do actually understand the difference between the two cases. What I'm saying is that in my experience hash tables that actually need to contain nil as a value are many times less common than those that don't.

In situations where the values you're storing might be nil, you just enclose all the values in lists.

My goal in Arc is to have elegant solutions for the cases that actually happen, at the expense of elegance in solutions for rare edge cases.

-----

1 point by mschw 6172 days ago | link

Python's defaultdict takes a factory function at construction time.

http://docs.python.org/lib/defaultdict-objects.html

-----

12 points by pg 6176 days ago | link

That's true. Fixing that is one of the top priorities.

-----

3 points by ryantmulligan 6176 days ago | link

Could you please elaborate on what Arc's version is? Personally I don't even understand what your CL code is doing, being a Lisp Newb.

-----

5 points by jimbokun 6176 days ago | link

(incf (gethash k table 0))

table is a hash table, k is a key, gethash returns the value in table for k or 0 if no value for k is found. Think of incf as ++. It will increment the value by 1 and set that as the value for k in table.

-----

7 points by pg 6176 days ago | link

And the problem with Arc is that currently the default value for an entry in a hash table is nil, rather than zero. If h is a hash table and you know (h 'foo) is 1, you can safely say

  (++ (h 'foo))  
But if you don't know whether (h 'foo) has a value yet you have to check explicitly:

  (= (h 'foo) (+ 1 (or (h 'foo) 0)))

-----

1 point by metageek 6176 days ago | link

How about if <code>h</code> takes an optional second argument, which is the default, and the macros are smart enough that you can do <code>(++ (h 'foo 0))</code>?

-----

1 point by greatness 6172 days ago | link

I agree, this is probably the best solution.

-----

1 point by reitzensteinm 6176 days ago | link

Perhaps (++ containsnil) should result in 1 anyway? Is there any case where that would break anything?

-----

2 points by simonb 6176 days ago | link

For one it breaks the expectation of a strongly typed language.

If something goes wrong and you want to fail as soon as possible not propagate the defect through the system.

-----

1 point by reitzensteinm 6176 days ago | link

Oh, it definitely throws strong typing right out of the window.

The reason I suggested it is because it would seem that almost all of the time where you go to do an increment on a nil value, you're working with an uninitialized element (not necessarily in a hash map) and treating that as 0 (as you're doing an increment) would in a certain sense be reasonable behaviour.

But I guess you're right, in the case where nil does represent an error, it'll be two steps backwards when you go to debug the thing.

-----

1 point by william42 6169 days ago | link

Or perhaps just set containsnil to 0 when you do that. (Knowing pg, this would probably work.)

-----

1 point by Tichy 6176 days ago | link

What is the usage scenario for that? I have never written such a code (incrementing values in hashtables) - maybe it is more common in LISP?

-----

1 point by bOR_ 6176 days ago | link

Happens when you want to categorize the frequency of items in a list, and I've been doing that all the time (categorizing gene frequencies in an agent-based model).

In ruby I'd extend the array class with this code

  class Array
    def categorize
      hash = Hash.new(0)
      self.each {|item| hash[item] += 1}
      return hash
    end
  end
although the other day I saw someone achieve the same thing using a hack on inject (the `; hash' part is only there because inject demands that, the work is done earlier.)

  array.inject(Hash.new(0)) {|hash,key| hash[key] += 1 ; hash}
Noticing that lisp / arc is more concise indeed. I'll have fun learning it.

-----

1 point by smallpaul 6173 days ago | link

Why would you extend rather than subclass the Array class? It kind of confirms all of my worst fears about Ruby's too-easy class reopening. (what happens when someone else defines an Array method called "categorize" for a totally unrelated purposes?)

I think that the Python syntax for this is

h[x] = h.get(x, 0) + 1

It isn't quite as concise as the Common Lisp but more so than Arc. I'd be curious to see what the Common Lisp looks like if you are doing something more complicated than an in-place increment. E.g. the equivalent of:

h[x] = h.get(x, 1) * 2

-----

2 points by bOR_ 6170 days ago | link

I'm a phd, working alone on projects, and the scripts I write a generally < 300 lines + 6 functions from a library I wrote. The agent-based models i write are ~ 200 lines, no libraries.

For me there's not much risk in redefining things.

-----

1 point by jsg 6173 days ago | link

(setf (gethash x h) (* (gethash x h 1) 2))

-----

1 point by ijoshua 6173 days ago | link

A hashtable containing integer values is a common implementation for the collection data structure known as a Bag or Counted Set. The value indicates how many instances of the key appear in the collection. Incrementing the value would be equivalent to adding a member instance. Giving a zero default is a shortcut to avoid having to check for membership.

-----