Closed Kinnardian closed 8 years ago
That Javascript syntax actually is creating an object containing tables, so the keys are implicit in the declaration of people
. In Arc you'd do:
(= x (obj name "Paul" age 55 hometown "Weymouth"))
(= y (obj name "John" age 93 hometown "Boston"))
(= people (obj x x y y))
(prn people!x!hometown)
Just to check my understanding object
s and table
s are the same thing in arc, right?
That's what I gathered from the documentation here: http://arclanguage.github.io/ref/table.html
Yes, they're the same. We prefer to say table everywhere rather than object. We should probably remove the one occurrence of the word 'object' on that page..
I find the (obj )
macro most-confusing. Why isn't that called (table )
table
is a function for initializing an empty table. obj
is a macro for initializing a table with contents. I agree, it's a little redundant. In Wart I had a single name. We can't overload the name in Arc because of Racket's constraints, but perhaps we
should rename obj
to table
and table
to something else.
The new table
could call the old table
in the event of no args and there would then be one macro for initializing an empty table or one with contents
Yup. Already obj
works for both purposes.
(obj )
wont take a variable as a key parameter will it?
(= nickname "pg")
(= pg (obj name "Paul" Hometown "Weland")
(= jfk (obj name "John" hometown "Boston"))
(= people (obj nickname pg jfk jfk))
(pr people!pg!name) ;; error
(pr people!nickname!name) ;; "Paul"
No, obj
quotes the keys. If I want a variable key I just do assignments in a loop or something.
(each person people-list
(= (people person.id) person))
That's unfortunate. I wonder what it would take to make it so that variable keys, assuming they were typecast/coerced to strings, worked. Sort of like the javascript bracket accessor:
var car={};
var property = "make";
car[property]="Toyotoa";
console.log(car.make); // "Toyota"
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_Accessors
It's not hard. All you have to do is remove one quote from the definition of obj
in arc.arc:
(mac kinnard-obj args
`(listtab (list ,@(map (fn ((k v))
`(list ,k ,v))
(pair args)))))
Would that break anything?
If you define obj
that way? Of course. But it's safe if you give it a new name and use it in your code.
If you don't want the argument quoted, you don't need a macro. Either call 'listtab directly or write a simpler function wrapping it. Maybe something like:
(def kinnard-obj items
(listtab:pair items))
D'uh!
(listtab )
doesn't appear to work on nested lists:
arc> (list "pg" '(("hometown" "weyland") ("age" 56)))
("pg" (("hometown" "weyland") ("age" 56)))
arc> (listtab (list "pg" '(("hometown" "weyland") ("age" 56))))
car: contract violation
expected: pair?
given: "pg"
context...:
/Users/Kinnard/Desktop/anarki/ac.scm:638:0: ar-xcar
map1
listtab
/Users/Kinnard/Desktop/anarki/ac.scm:1251:4
Alternatively:
arc> (listtab (pair "pg" '(("hometown" "weyland") ("age" 56))))
Can't take cdr of "pg"
context...:
zz
/Users/Kinnard/Desktop/anarki/ac.scm:1251:4
But that's coming from (pair )
Yes, it can't know what you want to do with inner lists. Do you expect (list 3 (obj a 4 b 5)) into a list of lists too? Functions operate on the top level by default.
In that case I'd expect a list (3 #hash((a . 4) (b . 5)))
if lists are allowed to contain objects/tables and (3 ((a 4) (b 5)))
if not.
So there's no straightforward way to create a nested table from a list or a string?
If you want to write a function that creates a nested table you can. It's just not very often useful, in my experience. Is there anything in particular you're trying to do? It's not like there's anything analogous to this in any other language either, is there?
Anyways, this is a good exercise :) Write a function that takes a nested list and turns all lists into tables :) (What should you do if any list has an odd number of elements?)
In that case I'd expect a list
(3 #hash((a . 4) (b . 5)))
if lists are allowed to contain objects and(3 ((a 4) (b 5)))
if not.
Yes, lists are allowed to contain objects. My point was this: if you don't expect list
to turn nested tables into lists, why would you expect table
to turn nested lists into tables?
You're using listtab
wrong in your previous example. It requires an association list, which I showed you before. It's a list of pairs.
arc> (listtab '((a 1) (b 2)))
#hash((a . 1) (b . 2))
I notice the online help doesn't have an example for listtab
. I'll add it now.
Sorry, I've been responding to your questions one at a time with no memory of the thread as a whole. Are you basically trying to create the original list of lists in a single line or something like that? Was my response in #1 not what you wanted for some other reason?
Nested tables just seem to be the norm in javascript from my experience. I've heard "Everything is an object" in js.
This does seem like a good exercise, though I loathe the timing. Here listtab
takes a list
: http://arclanguage.github.io/ref/table.html
I wonder if there's ambiguity between al
s and list
s throughout those docs.
I think the idea of the original author was that "list of key/value pairs" would be clearer than "association list".
BTW, here's where that terminology comes from, in Common Lisp: https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node153.html
Nested tables just seem to be the norm in javascript from my experience. I've heard "Everything is an object" in js.
That is true. I think you meant to express your original example in Javascript as:
var people = {
bob: {
name: "Bob Sampson"
age: 55
hometown: "Bangor, Maine"
},
charlie: {
...
}
}
Yes, Javascript has the edge here, because Lisp has no literal syntax for anything but lists (and strings). But now show me how you'd construct a nested object of objects in Javascript without curly brackets. What standard functions are there to construct objects out of lists?
The syntactic restrictions and the uniformity of using only parentheses for all syntax have their costs. This lack of syntax for literal objects is one of them.
And yes, the al
argument is intended (in the absence of type information) to convey that the argument is expected to be an association list.
Ack, I think I misused "D'uh" before, @shader. I meant it in the sense of "I can't believe I missed something so obvious!" :)
I don't think you really want to have a function that converts any nested list structure to a nested table structure; what if you wanted some of the values to be lists?
The right answer is to do it like in JavaScript, and pass tables as values. They just have syntactic sugar for objects. The closest in arc would be something like
(obj
a (obj 1 2)
b (obj 3 4))
I had wanted originally to do something along the lines of:
(= age 450 hometown "Cambridge" username "nwtn" name "Isaac Newton")
(= (people username) (obj username username name name age age hometown hometown ))
(save-table people peoplefile*)
That appears to be working.
I had at first attempted (obj (obj ) )
approach but that gave me the problem of passing a in variable key.
https://github.com/arclanguage/anarki/issues/46#issuecomment-203178795
That lead me to employ (listtab )
per @shader recommendation but that turned out to be a rabbit hole of sorts since listtab works on association list
s, not on list
s or on nested lists for that matter.
https://github.com/arclanguage/anarki/issues/46#issuecomment-203186605
This does work for variable key assignment:
(def var-key-table items
(listtab:pair items))
But the whole problem was obviated by the (= (people username) . . . )
above. What's this sort of lookup called? It probably should have been clear to me that I could take this approach from @akkartik's recommendation: https://github.com/arclanguage/anarki/issues/46#issuecomment-203181732 But it wasn't.
Broadly it seems I need to get a better understanding of list
s and table
s in arc and it seems the functionally could be augmented with more methods or perhaps a utility library.
@akkartik et alia, do you think it would be possible to implement a new "table literal" special syntax using curly braces? {name "Rick James" occupation "Singer"} ==(obj name "Rick James" occupation "Singer")
Really what I wanted (and still want) was an associative array . . . with arbitrarily deep nesting
@akkartik et alia, do you think it would be possible to implement a new "table literal" special syntax using curly braces? {name "Rick James" occupation "Singer"} ==(obj name "Rick James" occupation "Singer")
I don't know. Lisps have something called reader macros and you might be able to build this in Racket. But I don't have much experience with them. Look at brackets.scm
sometime when you understand Arc better.
But it doesn't seem worth the trouble. Is obj
really so much more trouble to write out? Would curly braces help much, given you'd still not be able to pass in variable keys?
I'd say spend some time learning what the language has and its idioms of combination before you try to change it. Don't compare it with other languages, accept it for what it is for a time. Not that I was ever very good at following this advice when learning a new language :)
Broadly it seems I need to get a better understanding of lists and tables in arc
Yes. Hey, have you gone over the official tutorial? http://www.arclanguage.org/tut.txt. It also applies to anarki except for one gotcha with for
, and it goes over lists and tables pretty well.
and it seems the functionally could be augmented with more methods or perhaps a utility library.
No, it's a failure of documentation. The methods are close enough to ideal that it wouldn't make much difference to your learning. Or it might help you but hinder someone coming from a different language. Or it might provide too wide a menu of choices which would confuse everyone even more.. Just suck it up :) This fitful process is what it takes to learn something new and different.
Go over the tutorial. Paul Graham is a better writer than any of us here.
Really what I wanted (and still want) was an associative array . . .
But table
does provide associative arrays, right? Is there any
functionality it doesn't have? You're just missing the syntax you're
used to, and you came to Lisp to leave behind the crutches of syntax and
learn the value of macros in return. Just suck it up, and type in (obj
where you'd type {
and )
where you'd type '}'.
The terminology seems convoluted everywhere:
http://cs.stackexchange.com/questions/6678/relation-and-difference-between-associative-array-and-hashing-table http://stackoverflow.com/questions/2884068/what-is-the-difference-between-a-map-and-a-dictionary http://programmers.stackexchange.com/questions/30908/difference-between-hash-and-dictionary https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map https://en.wikipedia.org/wiki/Associative_array
So it might be better to describe behavior:
An arbitrarily nestable key-value structure that allows "pushing" and has a homoiconic instantiation and where a uniques constraint can be imposed on keys the way an "array
" can only have one 0 index and one 1 index. I'd only be interested in the uniqueness constraint on the first level in this case and I think regular arc tables
would work for anything deeper. I think arc's templates
come into play somewhere here but I was having trouble with them and still learning tables
anyway.
Sort of like
person= {
name:string
age:number,
hometown: string
};
pg= new person("Paul Graham", 56, "Some town in England");
var people={};
people.push(pg);
I am still getting used to arc
and lisp in fact. Still breaking the "thinking in other languages" habit.
Maybe it won't help, but here's my attempt at disambiguating the nuances between the terms:
As for your desires and examples, I'm not sure I understand the problem. There are many differences between arc and other languages, but the hash tables are not one of them. They're really quite standard.
You may be running into an issue with the fact that lisp has symbols as a separate literal type, while most languages only have strings and sometimes support syntactic sugar to interpret barewords as strings. JS does that for example. Honestly, I prefer the support for symbols, and would much prefer a JS that had them as well.
Another thing I don't quite understand is your desire for the ability to 'push' - JS objects do not actually have a push method, nor do any other map / dictionary / hash table types that I know of. JS arrays do, but they are really wrapper objects around hash tables that implicitly map sequential numbers to anonymously injected items. So a = []; a.push('foo'); a.push('bar')
produces an object {0: foo, 1: bar}
. I'm sure you could create such a wrapper around an arc obj if you wished; it is likely not what you actually want however.
Normally insertion into a hash table is done as stated above, through explicitly setting a key to match a value. Some implementations are stuck with methods like tab.insert(k, v)
, but many languages support direct assignment like tab[k] = v
- this is how we usually do it in arc as well. You can do it explicitly with (sref tab k v)
, but usually we take advantage of some magic to use (= (tab k) v)
or more succinctly (= tab.k v)
and (= tab!key v)
.
There are two layers to those methods. The first is that we can overload application of non-functions to do other things, such as looking up a key in a table. That's why (tab k)
actually looks up k in tab, instead of complaining loudly about trying to apply something that's not a function.
The second is that '= is extendable to detect the form of whatever place it's assigning to, and use the appropriate setter. This makes for very symmetric code, as you can use the same expression for assignment and lookup, almost like pattern matching.
In the end, I'm not really sure what you're having difficulty with, but I hope something up there helped a little. I'd also recommend trying to stay away from mutating data structures as much as possible, but that's somewhat unrelated.
Templates are somewhat like object prototypes, but they don't provide much besides type tagging and default values. You still have to explicitly identify each property when you create them, so I sometimes define a constructor to go with them for brevity.
(deftem person
name nil
age nil
hometown nil)
(= people* (table))
(mac new-person (id name age hometown)
`(= people*.id (inst 'person 'name ,name 'age ,age 'hometown ,hometown)))
(new-person pg "Paul Graham" 56 "Some town in England")
It would be easy to add more helpers around building these things, but it's hardly worth it.
Thanks @shader that all does provide some additional clarification. It wouldn't have occurred to me to employ a macro; I'm still getting used to having them as an option and understanding when they're the right choice.
This case doesn't really need a macro; all it does is prevent evaluation of the ID, making it a symbol. There are much better examples for when to use macros elsewhere.
Generally macros are the right choice when you want different syntax, assignment to one of the names you pass in, or more control over the evaluation of another form. pg's book On Lisp has lots of really good examples, and the chapter on macros in Practical Common Lisp is also pretty good.
One of the common patterns is a "with" macro, that provides context to some code, like w/infile:
(mac w/infile (var path body)
`(let ,var (infile ,path)
,@body
(close ,var)))
This both provides a variable during the execution of 'body, and does extra work before and after.
That was a bit of a tangent though.
On Thu, Mar 31, 2016, 23:25 Kinnardian notifications@github.com wrote:
Thanks @shader https://github.com/shader that all does provide some additional clarification. It wouldn't have occurred to me to employ a macro; I'm still getting used to having them as an option and understanding when they're the right choice.
— You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub https://github.com/arclanguage/anarki/issues/46#issuecomment-204229429
What's the best (or even a good) way to build nested tables in arc? For example in Javascript this is pretty straightforward:
I'm having quite a bit of trouble doing something analogous in arc.