freenode/#lisp - IRC Chatlog
Search
22:15:28
jcowan
Well then, why bother to have external symbols at all and require a (slightly) more verbose syntax to access them from other packages? (This is not a rhetorical question.)
22:34:02
skidd0
to get access to the lack.response, would I want to do something :around the CALL method?
22:56:39
Bike
skidd0: it doesn't replace, but if your :around method doesn't call-next-method the original method will not be called
23:56:10
skidd0
I'm getting an error where hunchentoot doesn't know how to handle options http requests
1:28:34
kooga
(setf (lack.response:response-headers *response*) '(:access-control-allow-origin "*"))
1:29:52
skidd0
also, i figured out that I needed to add a options verb to the routing library i'm using (snooze)
4:00:27
beach
In a first-class global environments, I have a table called slot-readers and a table called slot-writers. These tables are indexed by symbol names.
4:00:29
Bike
in ecl and clasp direct instances of standard object use a hash table from slot names to locations.
4:01:19
beach
... then those tables work like the function entries in the first-class global environment.
4:02:22
beach
slot-value with a constant slot name gets rewritten to (funcall (car (load-time-value (find-reader-cell <name>))))
4:03:19
beach
I think SBCL must do the same, modulo the first-class global environment, and the function cell.
4:08:29
Bike
also wait, what happens if you have like (defclass foo () ((x :reader foo-x))) (defclass bar () ((x :reader bar-x))) (slot-value some-object 'x)
4:11:46
beach
In my scenario, (slot-value some-object 'x) would be translated to something like (funcall (car (load-time-value (find-reader-cell 'x))) some-object) and the cell would contain a generic function that dispatches on the class of some-object.
4:11:55
Bike
if the effective method just calls the accessor method, the discriminating function just reads from or writes to the slot directly
4:15:22
Bike
also the fast path i mentioned only happens if it's an object standard enough to not have custom slot-value-using-class methods possible. forgot that caveat
4:15:35
beach
Also, until a few years ago, I used slot-value for "internal" access, and slot accessors only for the public interface.
4:16:29
Bike
i'm not sure what the main reader method can do besides calling slot-value-using-class, i guess
4:18:17
beach
He had classes where slots were implemented as special variables so as to make it thread safe.
4:18:55
aeth
What I tend to do for internal setting is :accessor %foo :reader foo because that way I can use with-accessors on %foo without surprising behavior (i.e. only being able to set)
4:20:59
beach
So, in that case, SLOT-VALUE can not be optimized to do a direct slot access, of course, since there is no slot-location.
4:21:36
Bike
i have been thinking on and off about adding more interdependencies between methods in fastgf. for example, within a method body replace accessors with direct slot reads when possible, and then recompile the method if there's a change so it works transparently
4:21:50
Bike
i bet it would make it faster, but it would be pretty involved to maintain everything correctly
4:26:01
beach
So, I think I'll have to add SLOT-READERS and SLOT-WRITERS to the SICL first-class global environments.
4:31:02
beach
This stuff is frequently done "manually": (defgeneric foop (x) (:method (x) nil) (:method ((x foo)) t))
4:33:03
beach
Bike: Let me take a break and think about that idea. I think better when I am not at my computer.
4:56:57
beach
minion: memo for Bike: This is totally brilliant. I am convinced that it works. 1. Figure out whether any implementation does it. 2. If not, figure out what happens to the methods and call history as a result of DEFCLASS, and DEFTYPE. 3. Write it down. 4. Submit to ELS.
4:59:20
beach
minion: memo for Bike: There would be three kinds of methods: always false, always true, and requiring some action (for example if there is a DEFTYPE that expands to a compound type).
8:12:34
makomo
hah, today is the day when i finally used a full spec instead of just a symbol for ONCE-ONLY
8:30:57
jackdaniel
. is just a notation for a data structure (like #(1 2 3) is for vector), it is not a function
8:34:01
no-defun-allowed
if you do (a (. b c)) you break the expectations of depth - is it (a b . c) or (a (b . c))?
8:35:13
aeth
If it was anything else it'd probably be #.(a b) but then you wouldn't have it combined with the list representation and (a b . c) wouldn't be an obvious representation for a dotted list
8:36:56
oni-on-ion
perhaps i dont see the purpose of (a b . c) aside from being a bit strange considering lispisms
8:39:05
oni-on-ion
phoe: yeah, but its just syntax for cons isnt it? thats the point here, why its in between a and b and not prefix like everything else of lisp system
8:39:12
phoe
(x y z a . b) denotes the same thing, except three conses are consed on top of that to form an improper list, with CARs X, Y, and Z
8:39:50
no-defun-allowed
*when you're parsing, you see a paren. ooh, a list! let's see the first element. A it is. see the rest and stuff it into a cons. you see a B. see the rest again. you see...oh god a period! just return the C. cons B and C, you have (B . C). cons A to it, you finish with (A B . C)
8:40:52
ggole
Seems like confusion between reader syntax, which directs the reader to construct list structure, and the functions used to construct it
8:42:43
phoe
once, to read the cons with CAR A, once, to read the cons with CAR B, once, to read the cons with CAR C
8:43:10
phoe
. tells the reader "okay, you don't need to cons more, just stuff this thing after the period in the CDR and you're done"
8:43:26
oni-on-ion
so there is purpose to it being the syntax in the form of "(a . b)" and not "(. a b)" ?
8:45:30
oni-on-ion
ahh =) i think i get it now =) thanks. i was just trying to sleep and thought of this. quite cool actually ^_^ always really liked linked lists.
8:47:21
phoe
the concept of . is lost by the time you leave the reader and you get the read expression
8:48:21
phoe
you can only infer that there was a . somewhere by parsing the lists made up of the conses, and by noticing that some CDR is a non-null atom
8:48:56
phoe
and in this case, there's no difference between the result of the function call (CONS 'A 'B) and the result of the reader reading "(A . B)"
8:49:30
phoe
so the same result, a cons whose CAR is A and CDR is B, can come both from a #'CONS call and from the reader's operations.
8:51:36
makomo
what does everyone think of the following "problem": say i want to swap two rows of a matrix and i want a neat set of places for my interface. so i define something like (mrow mat i) which returns a fresh vector representing mat's i-th row, and ((setf mrow) vec mat i) which sets a mat's i-th (receiving a vector as a value and doing it element-wise).
8:51:39
makomo
but then, let's say i want to swap a mat's rows without creating an intermediary vector. (rotatef (mrow mat i) (mrow mat j)) would create a fresh vector since it evaluates (mrow mat j) before assigning. i could define my own (swap-row mat i j) but then that doesn't play nicely with places.
8:51:41
makomo
in general, the problem is that setf isn't "fully composable", because the expansion for a place cannot depend on the the form that produces the value to set. for example, i would like for (rotatef (mrow mat i) (mrow mat j)) to expand into a fully in-place swap of the rows (composition of smaller rotatefs). (rotatef (mrow mat i) (get-some-vec)) however would behave just as before.
8:51:43
makomo
so basically, we would have the ability to analyze the value-producing forms as well and generate expansions of places using that information.
8:55:21
makomo
i don't know whether this would make SETF better or worse. you get more composability and flexibility but perhaps SETF would become irregular, as you would have no proper separation of (1) eval the value-producing form and (2) set the place's value using its expansion and the produced value
8:58:32
makomo
so you could encode any conceptual "assignment operation" into a SETF call, and let the expander generate code that would do that work
9:00:09
makomo
phoe: yeah, but that's what the thought experiment is about. the ordering between individual assignments would still stay, but there would be no separation between the two phases of getting the value and putting the value somewhere
9:00:59
makomo
because an expansion might be able to do a better job when there is no separation (in this case, i wouldn't have to cons a new vector just to swap 2 rows)
9:01:11
phoe
theoretically you have control over SETF in form of define-setf-expander, but I think it would be somewhat confusing.
9:01:33
makomo
phoe: that's still not enough, because a setf expander doesn't have access to the value-producing form
9:01:53
phoe
for example, the expansion of (setf (subseq #(0 1 2 3) 0 2) (subseq #(5 6 7 8) 0 2)) also conses up a new vector.
9:02:18
phoe
this could be replaced by a very small and efficient loop, but SETF is too limited to be able to do that.
9:05:52
aeth
Notice that (1) the row size is in the name of the setf and (2) I use multiple values as the input
9:05:56
makomo
i thought about VALUES as well, but it won't work with "compound objects" that have a large number of "components"
9:09:26
aeth
Swapping a row is pretty simple if you're willing to put it in a function. Very elegant, actually, since you can just swap the elements via PSETF in a loop.
9:09:35
makomo
the cool thing is that this "composable setf" could also expand into further such setfs, etc.
9:10:34
makomo
aeth: true. that's what i ended up doing in the end, but it doesn't play nicely with places. also, i have NxN matrices, so i can't use PSETF
9:10:54
makomo
unless you were thinking of using PSETF for the swapping of the individual elements, in which case you can just use ROTATEF
9:13:16
makomo
now imagine a composable ROTATEF, you could swap literally anything without inducing overhead. swapping two matrix rows would just expand into multiple ROTATEFs between the individual elements
9:15:46
aeth
(let ((m (make-array '(2 4) :element-type 'single-float :initial-contents '((1f0 2f0 3f0 4f0) (5f0 6f0 7f0 8f0))))) (rotatef (array-row-of-4 m 0) (array-row-of-4 m 1)) m) => #2A((5.0 6.0 7.0 8.0) (1.0 2.0 3.0 4.0))
9:16:02
russellw
If I do (setq x 1) at the top level with no previous declaration of x, it actually works, to my surprise. What kind of variable does that create? Is it a special variable, the kind that would be created by defvar?
9:17:39
makomo
russellw: it probably does ""the right thing"" and proclaims the variables special, but i don't know for sure
9:18:16
makomo
aeth: that's a nice trick for a small number of components, but for larger Ns, you're back at consing and what not
9:18:21
aeth
makomo: https://gitlab.com/zombie-raptor/zombie-raptor/blob/b90f23cf6168f892fce8fd980649eaf882662acb/util/array.lisp
9:18:23
heisig
makomo: You could use a compiler macro for (setf mrow) that eliminates the intermediate copy if given a call to mrow as an input.
9:19:17
aeth
makomo: the consing threshold is probably higher than you think, though, and might be entirely optimized away.
9:19:58
makomo
heisig: hmm, interesting idea. but do i have the guarantee that SETF will call my setter with the value-producing as is, instead of say, evaluating it, storing it into a local variable and then passing that variable to my setter
9:20:56
|3b|
i think at least one implementation does something else though, maybe one of the commercial ones?
9:21:34
makomo
aeth: yeah, probably, but i'd still like to have a general mechanism that could handle a large number of components as well
9:22:51
aeth
(well, I set up the loop in a macro rather than do the loop at runtime... should be better for 4, probably not for 16)
9:32:23
heisig
makomo: A combination of compiler macros and a custom, composable SETF (CSETF?) might work. I will think about this, because I really like the idea of doing (crotatef (row a i) (row b j)).
9:35:11
makomo
heisig: however, if you're already "reinventing" setf, i.e. implementing your own, do you really need compiler macros then?
9:35:27
pjb
oni-on-ion: you must be careful, people write confusing things. For example, <phoe> (list* a b c d e f) ;=> (a b c d e . f) is wrong. Actually, (list* a b c d e f) -> (the-value-of-a the-value-of-b the-value-of-c the-value-of-d the-value-of-e . the-value-of-f)
9:35:39
makomo
you just write your own mechanisms of registering an "assignment expander", which takes into account both the destination and the source forms, and generates whatever
9:37:30
pjb
oni-on-ion: in lisp, things can be computed at different times: read-time, compilation-time, macro-expansion-time, load-time, run-time. And with the use of #. eval compile load-time-value and other operators, you can freely embeb one time in the other.
9:38:17
pjb
But basically, syntax, and reader macros, are read-time operations. The purpose is to obtain a lisp object that has been "read" from some characters with some syntax.
9:39:03
pjb
Once the reading has been done, the lisp object has been created, and it becomes a literal object. (it should be considered immutable).
9:39:10
russellw
Are there any pitfalls with eval that I should watch out for? I have tested it on a complex form including defstruct and defun, and disassembled a resulting compiled function, and everything seems to work just the way it would if the evaluated code had been part of the program in the first place
9:39:17
pjb
If an implementation provided operators to make immutable objects, reader macros could use them.
9:39:34
pjb
russellw: CL:EVAL doesn't take an environment argument, so it works in the global environment.
9:40:12
pjb
oni-on-ion: there's however a trap here: a reader macro can read not the final object you'd want, but instead a sexp, which when evaluated, will produce the final object!
9:41:37
pjb
#P reads a pathname. Also, this means that you cannot read eg. a logical pathname without having first defined the logical hosts. Sometimes, you'll have to use logical namestrings instead of #P.
9:42:06
pjb
On the other hand, #'foo reads the expression (CL:FUNCTION foo). This expression needs to be evaluated to obtain the function.
9:42:37
pjb
Notably, if you use #' in a literal list, it won't be evaluated, so you won't get a function in there!
9:43:26
pjb
(defparameter *my-funs* '( #'sin #'cos #'tan )) ; wrong! Also, it's confusing because the printer usually prints (CL:FUNCTION foo) as #'foo: *my-funs* #| --> (#'sin #'cos #'tan) |#
9:43:59
pjb
(defparameter *my-funs* (list #'sin #'cos #'tan )) ; you need a run-time list; then: *my-funs* #| --> (#<Compiled-function sin #x3000000AC95F> #<Compiled-function cos #x3000000AD26F> #<Compiled-function tan #x3000000AE64F>) |# this is a list of functions!
9:44:30
pjb
Of course, you can evaluate #'foo only if foo has already be defined. If not, you have to keep the function name 'foo instead of the function itself #'foo.
9:45:03
pjb
Then you can use it in a literal list: (defparameter *my-funs* '(sin cos tan)) *my-funs* #| --> (sin cos tan) |# a list of symbols, naming functions, known or future.
9:45:55
heisig
makomo: I don't know whether one needs compiler macros here. I like them because you can get the (foo x) case fast, while still allowing the (apply #'foo (list x)) case.
9:45:58
pjb
So <phoe> whereas #'cons, #'list, #'list* are functions is wrong too. #'foo is NOT a function, it's a list: (type-of (quote #'foo)) #| --> cons |#
9:46:38
pjb
#'foo EVALUATES to a function, if a function is defined with that name, or signals an error: #'foo #| ERROR: Undefined function: foo |#
9:47:49
pjb
russellw: EVAL is only good to implement a REPL or LOAD. If what you're doing could be done at the REPL, or by saving the sexps in a file and loading it, then you're good.
9:49:11
russellw
pjb, right; I'm working on a machine learning project, so if the generated code ends up working the same as it would if I had written it in a file and loaded it, this is good
9:51:30
pjb
But the machine learning program cannot call or use those new functions and types directly.
9:52:48
russellw
well, except for calling the 'do-stuff' function, where do-stuff is by convention the name of the entry point to the generated code
9:52:50
pjb
Anyways, if you generate defstructs and other types, you need to use EVAL to integrate them to be used by your generated functions.
9:53:55
pjb
For the functions you may consider just defining them as anonymous functions that you would keep in your data structure and call them from there with funcall or apply (setf (gethash (generate-function-identifier) *funcs*) (compile nil `(lambda () ,expression)))
9:54:34
pjb
If you use eval defun, you may want to intern the name of the functions in a specific package, to avoid redefining a function of the program.
9:57:09
pjb
Now for the problem of closures , you can always have something like: (let ((x 42)) (eval `(let ((x ,x)) (defun foo () x)))) ; ie. you re-create an independent closure with EVAL. Since the outer let was compiled in your program, it couldn't know that you would need it as a closure for a run-time generated function. So you can only recreate a similar closure at run-time.
9:57:53
pjb
So while eval accesses only the global environment, you can still use it to define NEW lexical environments.