freenode/#lisp - IRC Chatlog
Search
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.
10:26:30
makomo
pjb: i have a perfect macro for that, EVAL-WITH: http://plaster.tymoon.eu/view/978#978
10:31:00
makomo
phoe: because i wanted to keep the usual syntax of EVAL (it being a function and evaluating its 2nd argument), but i'm not sure why i put a &body there
10:43:05
beach
scymtym: Do you happen to know whether SBCL optimizes TYPEP with a constant atomic type specifier in a way similar to what Bike described?
11:07:12
makomo
but it's a weird macro nonetheless. it kinda looks like a LET, except that the 2nd argument is evaluated to produce a body... for EVAL to eval
11:08:08
pjb
(funcall (let ((a 1) (b 2)) (eval-with (a b) (lambda () (list (incf a) (incf b)))))) #| --> (2 3) |# would be a more typical usage.
11:08:21
phoe
EVAL should not be used unless absolutely required - if you absolutely need to create and evaluate Lisp forms at runtime
11:08:40
pjb
(loop :with fun := (let ((a 1) (b 2)) (eval-with (a b) (lambda () (list (incf a) (incf b))))) :repeat 3 :collect (funcall fun)) #| --> ((2 3) (3 4) (4 5)) |#
11:08:44
makomo
oh, this isn't for anything specific, it's just an old macro i wrote. pjb mentioned a use case above so i thought of it
11:09:35
pjb
phoe: actually, it would be more like: (loop :with fun := (let ((a 1) (b 2)) (eval-with (a b) `(lambda () ,(progn (write-line "enter a sexp using a and b:") (read))))) :repeat 3 :collect (funcall fun))
11:10:35
makomo
right, so it doesn't really make sense for EVAL-WITH to not evaluate the 2nd argument
11:10:39
pjb
My example above with lambda, since it's not quoted, actually refers to the variables in the compilation-time closure!
14:02:14
beach
didi: In combination with WITHOUT-ERRORS it can be used to write a very simple function for determining whether a list is a proper list.
14:05:05
didi
I'm yet to use an improper list. I can imagine there's an use case for circular lists, but for what would one use a dotted list?
14:05:46
pjb
didi: actually, once I had a case, were I didn't want NIL as the end of the list. IIRC, I had to distinguish two cases for the end of the list.
14:06:09
pjb
But otherwise, it's more often an error, indeed. Hence the use of ENDP and list-length.
14:06:47
pjb
notably, length may do an infinite loop on circular lists, so if you have them, better use list-length (or my list-lengths which gives more info).
14:16:58
jcowan
didi: my implementation of lazy sequences uses improper lists to represent an incompletely materialized sequence, with the generator function in the cdr of the last pair.
14:17:48
jcowan
you can cdr down the already realized values, and when you get to a pair whose cdr is nil then you are done, but if you get a function then you call it and add a new pair.
14:20:08
jcowan
See https://github.com/scheme-requests-for-implementation/srfi-127/blob/master/lseqs/lseqs-impl.scm (in Scheme, but should be readable to any Lisper), specifically the lseq-cdr procedure
14:25:15
jcowan
Scheme returns a dedicated "end-of-file object" when any input function reaches end of file (rather than raising a condition) and I use the same convention for generator functions.
18:02:10
anamorphic
Hmm how would I ensure a text file I write to always produces Unix-style line endings, even on Windows?
18:05:39
jcowan
anamorphic: https://bugs.launchpad.net/sbcl/+bug/310185 suggests that sbcl always writes LF only, even on Windows, though this may have been fixed and not recorded there
19:24:56
solene
hello, is it possible to use a pipe command like echo foo | wc using uiop:run-program?
19:54:13
mrblack
what would be a good lisp project (that is not too hard) for the Linux environment that would solve things lispers need?
19:59:32
aeth
solene: you might want launch-program instead because it has a :stream option for :input and :output (but it's harder to work with because it's async).
20:00:30
solene
aeth: I'm quite happy with run-program, I just wanted to use a list because it prevents errors in passing parameters
20:05:04
mrblack
solene, I had this idea of implementing some of the gnu coreutils because hackability... but I don't know if that makes sense.
20:05:43
aeth
(defun uiop-pipe () (let* ((echo (uiop:launch-program "echo foo" :input :stream :output :stream)) (echo-output (uiop:process-info-output echo)) (wc (uiop:launch-program "wc" :input echo-output :output :stream)) (wc-output (uiop:process-info-output wc))) (loop :for line := (read-line wc-output nil :eof) :until (eql line :eof) :do (write-line line))))
20:19:33
aeth
Even though launch-program is technically async, this sort of resyncs it and makes it basically the same as run-program. run-program doesn't have :stream for some reason, possibly because :stream could hang.
20:26:09
aeth
It looks like these are the potential errors: https://github.com/fare/asdf/blob/b6d2f9b44c047a5e65520692159cab7d3e9e072e/uiop/launch-program.lisp#L507-L532