libera/#commonlisp - IRC Chatlog
Search
9:08:37
dnhester
Hi, I'm using `(get-properties place indicator-list)` and I'm interested in the `tail` which is a plist based on the indicator-list, and for some reason it's returning the first value, then the second, and third, but not as a list, no matter what I do, I can't access the tail, I've even wrapped the function in a (list ) and it still just has the
9:22:22
splittist
dnhester: the point being, GET-PROPERTIES is returning three values, not a three-member list
9:39:06
splittist
dnhester: if you want just the tail, then (NTH-VALUE 2 (GET-PROPERTIES ...)) will work.
9:46:42
dnhester
splittist I tried `(third)` and it didn't work. I just figured now using `(multiple-value-bind)` works, but will try doing the nth-value, I thought that was to access the nth element of a list
9:49:29
splittist
dnhester: you can have a look at http://www.lispworks.com/documentation/HyperSpec/Body/03_ag.htm for more on multiple values
9:50:02
dnhester
What is the difference between `:` and `#:` as prefixes to symbols, I'm trying to google it but google is not being nice with my quotes, so the results are lousy, any docs will be appreciated!
9:51:57
dnhester
@splittist btw this is extremely confusing, why would they do this for `get-properties` if the whole point is to get the tail, if I wanted to get the first or second return value I would just use `getf`
9:52:21
dnhester
Also, why wouldn't a function just return a list instead of having to use a special form??
9:54:41
semz
dnhester: :foo is the (unique) symbol of name "FOO" that is interned in the "KEYWORD" package. It will always* be the same symbol every time it is read. #:foo gives you a fresh symbol of the name "FOO" that isn't interned anywhere. It'll be a different symbol every time it is read. So you'd have (eq ':foo ':foo) but never (eq '#:foo '#:foo).
9:54:54
semz
* barring unintern shenanigans, but please don't unintern things from the KEYWORD package
9:59:25
dnhester
@semz nope, still no luck. for some reason I have a string, and I have to provide a symbol with the name of that string, but when doing `make-symbol` I was getting a `#:ID` instead of `:ID`, but doing `intern` is not giving me `:ID`
10:01:00
semz
INTERN defaults to the current package, but the package for symbols like :foo is called KEYWORD. (intern "FOO" "KEYWORD") would produce :foo
10:03:09
dnhester
@semz finally, it works, thanks for the explanation. Why are only symbols interned in KEYWORD have the colon, whereas symbols interned elsewhere do not?
10:09:20
beach
dnhester: You are confused. The colon character is not part of the symbol name. A symbol qualified by its package is usually written <package-name>:<symbol-name> and the colon is the "package marker". The reader uses special syntax :<symbol-name> as a shorthand for KEYWORD:<symbol-name>.
10:10:24
beach
dnhester: The reasons not to always return a list are: 1. Then memory would be allocated every time the function is called, and 2. A caller who wants to use only the first value would then have to take apart the list, whereas with multiple values, the remaining values can just be ignored.
10:53:51
MetaYan
beach: Thanks for the well-formulated explanation about multiple values. Caused a nice flash of insight here.
13:17:37
beach
I see. I know of no existing function to do the recursion for you. But maybe it exists.
13:17:50
splittist
also exported from asdf (so you can say asdf:system-depends-on). At least, it used to be.
13:57:33
beach
I said, if it returns a list, it *does* allocate. I said nothing about the other case.
14:01:14
beach
jcowan: It would have been much better if you had told dnhester that, which many high-quality implementation will manage to avoid allocating memory, at least for not too many return values, with multiple values, it would not be generally possible to avoid allocation if a list were to be returned, rather than telling me something I did not say, and that I fully understand since I am writing a Common Lisp system.
14:03:07
beach
jcowan: And I am pretty sure, or at least I hope, that you know that I do know these things, so that you already knew that it was unnecessary to tell it to me.
14:07:22
jcowan
Of course. But since dnhester wasn't connected at the time (they are now), I couldn't tell them anything, and I was pointing out (somewhat elliptically) that what you said was misleading (as distinct from it being wrong).
14:09:55
splittist
What is the history of multiple values? Are there any other languages that provide such a facility? (He asks desperately.)
14:10:31
jcowan
Once was enough. But since you take offense when I did not intend to give it, I will not mention your errors in rhetoric any more, though they are just as much errors as errors in grammar or logic.
14:13:41
beach
Oh, so you really meant something like "Your remark implicitly suggests that multiple values do not require allocation, but you obviously know that that is not always the case, so you should be more careful in the future to make sure no such implicit suggestions are made, so as to avoid misleading people like dnhester"? That is quite different.
14:15:03
beach
Well, then, you should be more careful in the future not to make a remark with a subject that is quite different from the one that you intended.
14:15:39
jcowan
splittist: Returning multiple values using variables passed by reference is as old as Fortran, and returning them using pointers is as old as Algol 68. But I know of no language that treats them specially other than the Lisps.
14:17:52
jcowan
splittist: Other than Scheme, I don't think so. Python speaks of returning multiple values, but they are
14:24:23
splittist
Or, from CLHS 1.1.2 "Sophisticated lambda lists, setf, multiple values, and structures like those in Common Lisp are the results of early experimentation with programming styles by the Lisp Machine group. Jonl White and others migrated these features to MacLisp."
14:31:26
jcowan
Lua has CL-style multiple values, except that wrapping a multiple-valued expression in (otherwise redundant) parentheses reduces it to a single value.
14:40:14
splittist
Depending on what "CL-style" means (: "There is absolutely no way in Common Lisp for a caller to distinguish between returning a single value in the ordinary manner and returning exactly one ``multiple value.'' For example, the values returned by the expressions (+ 1 2) and (values (+ 1 2)) are identical in every respect: the single value 3." (CLtL2 7.10.1)
14:41:55
dnhester
On the point above (this is extremely confusing, why would they do this for `get-properties` if the whole point is to get the tail, if I wanted to get the first or second return value I would just use `getf`) though, beach do you have any idea? It seems like a blunder to me
14:43:25
beach
dnhester: It is also useful if you just want the first indicator among a list of indicators passed as a value.
14:46:12
dnhester
Btw, an unrelated question: I have a Object which I'm encoding into JSON, and I have a list of objects to add to that object (because of a hasMany type relationship in the DB), how would I go about adding it to the Object before it gets encoded into JSON so that it's encoded as part of the object in JSON?
14:48:00
jcowan
splittist: That rule holds in Lua as well. By "CL-style" I meant that if more than one value is returned where one value is expected, all but the first value are implicitly dropped; if no values are returned, the value NIL is supplied. In Scheme, both of these situations are undefined-behavior, and most Schemes report an error.
14:48:24
dnhester
beach I hear, but wouldn't that be easier with `(first indicators)` and `(getf (first indicators) plist)`, I understand it's two functions, but `get-properties`'s definition seems to be mainly for the `tail` case not for the first two return values, those seem like after thoughts after reading the definition of `get-properties`
14:48:43
jcowan
Conceptually, returning multiple values is just the dual of accepting multiple arguments: if you return multiple values to your caller, it is equivalent to invoking your continuation with multiple arguments.
14:48:59
jcowan
ML and Haskell use currying so that all functions accept one argument and return one value.
14:49:43
splittist
jcowan: OK. But doesn't Lua require the extra parens around the call exactly in the case of expecting a single value and getting multiple?
14:50:08
beach
dnhester: You probably mean that your object is represented as an instance of a standard class. Then your class should have slots for the things you want added, no?
14:50:39
beach
dnhester: I am a bit lost as to why you talk about plists when you want to turn an instance of a standard class into a JSON thing.
14:51:33
dnhester
beach, since it's a class that persists to a DB, and it's a 1-N relationship where the N table holds the reference to the 1 table, but when encoding it to JSON I want to include all the N values as a list part of the 1 object. I hope this is clear
14:51:44
jcowan
No, in the sense that if "foo" returns 3 values, then "1 + foo()" will add 1 to the first value returned. The extra ()s are wanted when you want your function to return exactly one value after it tail-calls something that might return more: "return (foo())" will always return one value.
14:51:47
beach
dnhester: The indicator in (FIRST INDICATORS) is not necessarily the first indicator on the plist that is a member of the INDICATORS list.
14:51:55
dnhester
beach, sorry, the plist comment was related to the previous conversations about `get-properties` a few hours ago, my bad
14:53:03
beach
dnhester: I think you miss the logic about GET-PROPERTIES. It doesn't find the indicator in PLIST that is first in INDICATORS. It finds the first indicator on PLIST that is a member of INDICATORS.
14:53:20
dnhester
beach ah, ok, now I get it (referring to the previous conversation about `get-properties`
14:54:26
beach
dnhester: With respect to your DB, I am lost. I no longer know to which object you want to add things.
14:55:29
splittist
jcowan: OK. I'm really not understanding the 'note' halfway down this page: https://riptutorial.com/lua/example/4082/multiple-results
14:57:19
beach
dnhester: From what you said, you want to add things to an object, and the object is an instance of a standard class. Then you just make sure you class has a slot for each thing you want to add, and then you just set the relevant slot.
14:58:10
beach
I don't see any other interpretation of adding things to an instance of a standard class.
14:58:11
splittist
dnhester: or just don't have an instance of a standard class between the result of the DB query and the JSON output
14:59:11
jcowan
splittist: Tables are evidently a special case in Lua (I didn't know that, it may be a recent feature)
15:00:08
dnhester
beach sorry, I'll try to explain better: I have an CLOS Object a1 I got from table A in a DB, I have a lists of objects related to the object a1 from a table B in the DB [b1, b2, ..., bn]. Now I would like to encode Object a1 to JSON, yet include the list [b1, ..., bn] as part of that JSON encoded object. Can I add a temporary field to an Object
15:00:08
dnhester
for it to be encoded? Should I create another class that inherits from the first class and has that additional field? I just found something in the docs that may be related https://github.com/fukamachi/mito#class-definitions it talks about ` :ghost` is that some concept in common lisp relevant to this?
15:00:20
jcowan
note the previous case of {foo()} insertng all the values rather than just the first value
15:00:36
pfdietz
If you wanted to dynamically add slots to an object, you could create a new subclass with those slots and change-class the object to that new subclass.
15:01:37
dnhester
pfdietz thanks, that's what I was wondering, I thought there might be a simpler way
15:02:37
beach
dnhester: I'll let others try to answer since I am still lost. Just one thing, the term "CLOS object" is meaningless since every Common Lisp datum is an object and an instance of a class, though not necessarily of a standard class.
15:03:54
dnhester
I just found the docs in the source code "Option to specify slots as ghost slots. Ghost slots do not depend on a database."
15:07:36
jcowan
beach: What do you think is the best terminology for instances of classes defined with `class`? (This is not a rhetorical question.)
15:10:08
beach
"standard object" won't do, because there are standard objects that are not instances of standard classes.
15:11:14
beach
Yes, well, if the implementation represents hash tables that way, that's what they are. But I would often like to be more precise about hash tables and use "system class", to account for the possibility that they are not.
15:11:46
jcowan
Right. So we might speak of "non-system classes/instances" or even of "user classes/instances".
15:12:39
beach
I guess so. I guess all user-defined classes are standard classes, unless I am missing something.
15:13:50
jcowan
I'm not sure of that. A class whose metaclass is not a subclass of standard-class would not be a standard class.
15:15:25
beach
bjorkintosh: The thing is that the standard uses the term "system class" which gives the implementation the choice of representing such a class in a special way, or using a standard class.
15:16:24
beach
bjorkintosh: It gets more complicated still, because a system class may be a subclass of STANDARD-OBJECT even though it is not a standard class.
15:17:59
beach
bjorkintosh: Most Common Lisp implementations probably represent most system classes in a special way, simply because they were initially written before the standard, so CLOS was not part of Common Lisp then.
15:18:33
beach
bjorkintosh: But for a new implementation like SICL, there are more choices available that will simplify the system.
15:20:27
jcowan
Okay. Then since "CLOS" is not a formally defined term, but is generally understood to mean the parts of CL that specify DEFCLASS/DEFMETHOD/etc., we may informally define "CLOS class/object" as equivalent to "user-defined class/object", i.e. "a class/object of a class that is defined by DEFCLASS", possibly qualified by "with a metaclass of STANDARD-CLASS".
15:20:32
beach
bjorkintosh: For example, HASH-TABLE in SICL is a subclass of STANDARD-OBJECT because it is represented in the same way as other standard objects. But it is not a standard class, because I don't necessarily want to allow things like CHANGE-CLASS or the creation of subclasses.
15:22:20
beach
jcowan: We could do that, but I don't like it, since the very concept of a class was introduced by CLOS. So, to me, every class is a CLOS class, which makes the term meaningless.
15:23:44
jcowan
What I had in mind earlier was that an implementation might provide built-in metaclasses other than STANDARD-CLASS, STRUCTURE-CLASS, or BUILT-IN-CLASS. I don't know of any.
15:23:58
beach
jcowan: Plus, you would then exclude classes that are created by MAKE-INSTANCE on STANDARD-CLASS not using DEFCLASS.
15:26:08
jcowan
beach: Hmm, I didn't consider that. I guess you can instantiate such a nameless class even though you can't subclass it.
15:27:35
beach
jcowan: You can also subclass it. You just pass a list of superclasses to MAKE-INSTANCE.
15:29:37
jcowan
I guess in practice you would only want a metaclass which is a subclas of standard-class.
15:30:53
jcowan
That's where Smalltalk keeps class variables: they are instance variables of the metaclass. Consequently, there is a distinct metaclass for each class.
15:34:18
jcowan
I should say, the metaclass of all classes tha tare not themselves metaclasses are distinct. The metaclass of all metaclasses is a special class Metaclass.
15:35:47
jcowan
so (class (class SomeClass)) == Metaclass for all (ordinary) classes. This is a fixpoint equation
15:37:06
pfdietz
make-instance is a generic function, so one can define additional methods as long as there's at least one argument that's not a direct instance of a standard class. So, you could not pass a list as the class name, but you could pass some object of a user-defined class.
15:42:28
jcowan
In ST rather than the dichotomy of standard vs. built-in, you have standard vs. meta: an ordinary class cannot function as a metaclass.
15:44:21
jcowan
THere are also magic methods: thus (if-then-else p (lambda () ...) (lambda () ...)) will not work if p is not a boolean and the other two arguments are not lambdas.
15:46:52
jcowan
Note that this is logically speaking implemented by method dispatch: in class True, it invokes the second argument and in class False it invokes the third argument. But in fact it is handled by the compiler.