freenode/lisp - IRC Chatlog
Search
9:10:55
hjudt
thanks. i have added a function to sb-ext:*exit-hooks*, which gets called. additionally setting sb-ext:*timeout* to a lower value than the default 60 also solves my problem that sbcl takes too long to shutdown.
10:38:53
no-defun-allowed
Pretty sure we call it cl, unless you want an editor that only works with gnu clisp.
10:39:20
jackdaniel
clisp is one of many common lisp implementations, abbrev for common lisp is CL not clisp
10:41:08
no-defun-allowed
I might write an editor after Netfarm and Magrathea, but it won't be very emacsy, as it'll work on abstract syntax trees or actual lisp structures.
10:42:26
jackdaniel
I believe that's what beach works on with Second Climacs (and that was discussed briefly a few days ago on this channel)
10:45:35
no-defun-allowed
I remember I made an image of it which also loaded a climacsrc for the hell of it and wrote a hello world extension.
10:46:50
no-defun-allowed
Also, it has a better out of the box experience than slime+emacs IMO, I still can't get company mode out of dumb "I see a symbol, I suggest that symbol" mode.
10:50:28
jackdaniel
clisp is written in c, ecl's core is written in c as well, sbcl is written in common lisp, ccl is written in common lisp, abcl is written in java
10:51:23
no-defun-allowed
SBCL and CMUCL are partly done in C for stuff like the garbage collector and the "everything went horribly wrong" debugger.
10:52:20
jackdaniel
Necktwi: here you have a brief listing of CL implementations: https://common-lisp.net/project/ecl/static/quarterly/img/vol4/all-hierarchy.png
10:54:22
no-defun-allowed
jackdaniel: CMUCL and SBCL do use C for the lowest level parts of their runtimes. I like scaring people with gencgc.c for example.
10:57:29
jackdaniel
no-defun-allowed: they have some modules written in C, that is not the same. but now I udnerstand what you mean.
10:58:53
jackdaniel
also saying SICL is a Common Lisp implementation may be considered a little bit of exaggeration given it is not complete, I think a more proper term would be: a set of modules written in CL
10:59:40
jdz
Necktwi: are you here to learn something or just reject every opinion that does not match your own?
11:00:08
shka_
Necktwi: common lisp is language standard, implementations are free to be written any way they like to. There IS NO reference implementation.
11:00:10
jackdaniel
no-defun-allowed: that doesn't make sense to me. GCL is working ClTl2 implementation with some ANSI parts (so in this sense it is a finished working project)
11:00:19
no-defun-allowed
jackdaniel: I probably wouldn't want to run CL without a garbage collector, so it's a fairly crucial component of a Lisp system.
11:01:21
no-defun-allowed
clisp is the shitslow one -- oh we're talking about standard by "I'm the leader implementation"
11:02:55
jackdaniel
Necktwi: this kind of discussion belongs to #lispcafe - I suggest all interested should move it there. we've already left CL land with it to some opinions about compilers.
11:03:33
no-defun-allowed
Necktwi: if you don't want a garbage collector, you can take it to #rust or #c++ probably.
11:40:02
dim
jackdaniel: idly trying ecl again, I see a lot of shifting a negative signed value is undefined [-Wshift-negative-value] in the output, hundreds of warnings when compiling lisp code
11:44:30
jackdaniel
shifting is for int integer <-> fixnum conversion (fixnum is an immediate), this is correct. we could possibly do something with a warning, but I'm not sure how to hid it
11:47:31
jackdaniel
sorry, I have two pans on fire (making dinner) can't help you more at this moment
16:33:14
matzy_
dunno who else has read it here (I'm assuming most people?), but I've been learning CL, and the second chapter on macros in Practical Common Lisp blew apart my brain last night
16:38:02
matzy_
beforehand i actually thought i kind of had a grasp on them, but it made it very confusing somehow
16:38:14
White_Flame
macros take source code, and return source code. This transformation happens before the involved functions are ever called.
16:38:49
White_Flame
so in (my-macro foo bar baz), even if foo/bar/baz are local variables, you get the source code symbols FOO BAR BAZ as parameters to the macro
16:39:11
White_Flame
yes, (defmacro my-macro (sym1 &rest others) ...) sym1 = FOO, others = (BAR BAZ)
16:40:04
White_Flame
&body is pretty much exactly like &rest, but tells the editor to indent it like a code body instead of as a data list
16:47:43
matzy_
but aren't all macro names throw away prefixes? like you dont use the name for anything besides calling it, right?
16:48:12
White_Flame
macros perform some transformation, usually because you want to generate more complex code given a simpler form
16:48:52
White_Flame
(defmacro sum (&rest nums) `(+ ,@nums)) would semantically perform a sum operation, though it's equivalent to #'+
16:50:37
pfdietz
matzy_: macros can perform arbitrary computations. They can be used for things that require preprocessors in other languages. Something like yacc, for example, can be done via a macro in Lisp.
16:51:06
pfdietz
A general rule though is that if it can be done naturally by a function, don't use a macro.
16:51:31
White_Flame
basically, as PCL mentions, write the short macro form and the expanded form that you desire. Then write code to generate one from the other
16:53:17
White_Flame
create this macro: (defvars *a* *b* *c*), which would expand to (progn (defvar *a*) (defvar *b*) (defvar *c*)) for any given list of variable names
16:55:45
pfdietz
You might also look at the things in lisp itself that are macros. Call macroexpand or macroexpand-1 on the (quoted) forms to see what they turn into.
17:01:22
White_Flame
pressing Alt-. in emacs when on the macro name will take you to the macro's source code
17:01:35
pfdietz
Right. They show what happens when macro expansion is performed at the (top level of the) form.
17:02:31
matzy_
that's what i wanted to see, their source. but then it'll be super helpful to see how they transform too
17:03:47
matzy_
White_Flame: ah crap my evil bindings overwrote that shortcut. do you know what the function is that i could call with M-x?
17:06:39
pfdietz
macroexpand-1 does one expansion. macroexpand keeps doing expansion (at the root of the form) until it's not a macro there.
17:10:44
another-user
hi, is there real world examples of advantage of clos over structs/plain functions?
17:11:09
White_Flame
yeah, (collide ball wall). Do you put the collision "method" on class ball or class wall?
17:12:04
White_Flame
regarding pklain functions, they can't dispatch on their parameter type (at least in the C view of things)
17:12:52
White_Flame
and CL multimethods can have before/after/around hooks, as well as the meta-object protocol which lets you define what an object, method, etc fundamentally is & does
17:14:58
another-user
White_Flame: yeah, but all those hooks can be implemented trivially with plain functions as well and to define my data i could use structs/maps
17:15:40
White_Flame
certainly you can turing tarpit any feature, by dumping in lots of programmer discipline burden and mandating specific framework call styles
17:16:06
jackdaniel
another-user: you could have heard the same argument about introducing functions from assembler programmers
17:16:47
matzy_
White_Flame: i feel like this should be easier than i'm making it out to be. i dont get how to repeat (defvar ) statements like that for each arg
17:19:02
another-user
White_Flame: regarding collide example, it's good point against oop actually, usually it comes up in game dev and there people use ECS instead of OOP
17:19:16
beach
another-user: I think you are going to have to trust us that there are tons of advantages of using generic functions and standard objects. You will understand the advantages once you start using these features.
17:19:56
shka_
another-user: CLOS is awesome, allows to write programs with unparalleled extensibility while still being understandable
17:19:59
beach
another-user: It is good argument against traditional object orientation where the class provides encapsulation and where there is only single dispatch.
17:20:12
matzy_
Bike: i actually tried that in slime, and it said y was unbound. but shouldn't ,@vars at least work right?
17:20:37
beach
another-user: It is not a good argument against CLOS-style object orientation, where the encapsulation is provided by the package system and we have multiple dispatch.
17:20:43
shka_
i tried to write example in a blogpost, but sadly i think that i failed to show advantage decently
17:20:50
Bike
matzy_: it's expanding into (defvar x y z), which is what you told it to expand into, but probably not what you want it to expand into.
17:21:25
matzy_
Bike: no, slime throws an error instead of expanding, and while i get it is not what i was trying to do, shouldn't it still work?
17:21:35
jackdaniel
clos brings a lot to the table. I have ambivalent feelings about this, because it abstracts many things away (so you don't have to think about these things), but also requires a lot of cognitive effort for programmer to understand the code - I'm not sure if using it for anything non-trivial simplifies or complicates programming
17:24:25
another-user
beach: White_Flame yeah, i totally agree on usefulness of generic functions, let me clarify why i'am asking this weird questions
17:24:31
ski
ACTION . o O ( `(define-syntax make-vars (syntax-rules () ((make-vars ?var ...) (begin (define-variable ?var) ...))))' )
17:26:11
pfdietz
another-user: one example I like is using slots of standard objects as caches. The slot is initially unbound, and when accessed a method for the SLOT-UNBOUND method fills it in. After that the stored value is returned without being recomputed.'
17:26:55
ski
beach : `syntax-rules' in Scheme. the interesting part here is the use of `...' in syntax patterns and templates, "automagically" expanding to what matzy_ wanted (without an explicit call to `mapcar'/`map')
17:27:04
pfdietz
Another interesting use case: you want to add some slots to objects, temporarily. Change their class to a subclass that has the extra slots, do your work, then change them back when done.
17:27:54
cgay
jackdaniel: requires a lot of cognitive effort compared to what, though? I personally find today's Java totally impenetrable for example, and a well-written (waving hands wildly) clos program pretty straight-forward.
17:28:05
ski
indeed. i suppose i was wondering whether someone had implemented such a syntax matching system (with `...'), for CL
17:29:25
Bike
i mean i assume "implemented hygenic macros" means "implemented something like syntax-rules"
17:30:10
jackdaniel
i.e a dump function is easier to follow than a generic function with couple of around methods, custom method combination and arguments being non-standard classes
17:30:32
jackdaniel
otoh dumb function doing the same will be longer (given both pieces are written by a good programmer)
17:31:19
cgay
jackdaniel: i was simply talking about the congitive effort to understand a program, whether clos or something else.
17:31:36
beach
another-user: You had better clarify fast if you want me to read it. Dinner is imminent. :)
17:31:53
another-user
i'm coming from clojure world and there people just usually use hash maps for state and plain/generic functions to operate on said state and i'm totally fine with that but i wonder if clos will give me some super advantage
17:32:01
ski
(at least, in general, those are quite distinct things. `syntax-rules' is a DSL, not an EDSL. there are hygienic macro systems which allow you to run arbitrary code)
17:32:39
beach
another-user: Again, you will have to believe us and try it. It is not going to be easy to give you examples that will be convincing.
17:32:40
another-user
and i wonder why clos wasn't included in clojure if it's so cool(no sarcasm or irony of whatever)
17:33:08
pfdietz
Well, clojure tried to be more functional, except at the very highest level. Different design approach.
17:33:25
beach
another-user: When I taught Common Lisp in New Zealand, I gave an example where having no auxiliary methods meant that an optimization required a change in the interface, which is not what you want to tell your customers before installing version 2.
17:34:42
cgay
Dylan simplified CLOS a lot, and I suppose that's what I'm actually thinking about when I say CLOS is easy to understand, not all the :before, :after, :around, MOP stuff.
17:35:17
another-user
do you know what books/blogposts i should read about clos for newbies(with clojure background)
17:35:29
fade
reading Art of the Metaobject Protocol makes CLOS quite transparent, in my experience.
17:36:25
LdBeth
ACTION when I say Haskell is easy to understand, I mean Haskell 98 but not other extensions made by GHC
17:36:30
it3ration
yeah yeah, working on it - spent the last year in clojure, finally making it through practical common lisp
17:37:24
matzy_
ok, dumb question time. when i'm writing a macro, and i have a CL standard function that i was to run while expanding the macro, how do i denote that vs one that i DONT want run during the macro (i.e. i want passed through the macro to the resulting form, to be evaluated there)
17:38:28
pfdietz
matzy_: the stuff that gets passed through will be in quoted constants (or equivalently not comma-escaped in backquote forms).
17:38:42
pjb
matzy_: how do you write a function that returns an expression calling a function, without calling that function?
17:39:44
pjb
matzy_: you can build this data with list operators, with backquote or by quoting a list literal.
17:40:53
pjb
matzy_: more often backquote or LIST, since othen the resulting expression must contain variable parts, coming from the parameters of the macro.
17:41:03
jackdaniel
another-user: the problem is the same as with many stars in C (check out three star programmer)
17:41:40
pjb
matzy_: for example, let's write a function that transform an expression such as (while <condition> do <something>…) into (loop :while <condition> :do <something>…)
17:41:48
shka_
another-user: here it is: https://sirherrbatka.github.io/blog/2018/10/14/classes-are-not-interactions/
17:42:48
ski
ACTION . o O ( "Quasiquotation in Lisp" by Alan Bawden in 1999 at <http://people.csail.mit.edu/alan/ftp/quasiquote-v59.ps.gz> )
17:43:06
pfdietz
Ah, like a 10x programmer? (defined to be a programmer who creates enough technical debt to keep 10 others busy)
17:43:42
pjb
(defun expand-while (form) (destructuring-bind (while <condition> do &body <something>) form (assert (eql while 'while)) (assert (eql do 'do)) (list* 'loop :while <condition> :do <something>))) (expand-while '(while (< a b) do (print a) (incf a))) #| --> (loop :while (< a b) :do (print a) (incf a)) |#
17:44:02
it3ration
is there some way to use reader syntax to get clojure-style [] / {} / #{} syntax for vectors, maps, and sets in CL?
17:44:47
pjb
Now you can write the while macro as: (defmacro while (&whole form <condition> &body <body>) (expand-while form)) (macroexpand-1 '(while (< a b) do (print a) (incf a))) #| --> (loop :while (< a b) :do (print a) (incf a)) ; t |#
17:47:07
pjb
matzy_: of course, you can substitute the expand-while function and "inline" it directly in the macro: (defmacro while (&whole form <condition> do &body <body>) (assert (eql do 'do)) (list* 'loop :while <condition> :do <body>)) (macroexpand-1 '(while (< a b) do (print a) (incf a))) #| --> (loop :while (< a b) :do (print a) (incf a)) ; t |#
17:47:57
pjb
matzy_: if you want to use backquote, it's orthogonal to macros. You can study it independently.
17:48:47
pjb
<> don't need to be escaped, since they don't have any particular signification by default for the lisp reader.
17:50:01
pjb
matzy_: you could set < to a terminating reader macro and then you'd have to escape it to keep it in a symbol name.
17:51:21
matzy_
pjb: yeah i forgot about that. i was thinking they had some syntactical meaning in your code, which was weird because i went through chapter 2 and never saw it mentioned as valid syntax
17:52:00
pjb
matzy_: people who write grammars often use <> to distinguish non-terminal symbols from terminal symbols.
17:52:59
pjb
Since the destructuring form does something similar to parsing an expression, I used that same convention to make it clearn what was to be terminal symbols, pure syntactic elements, from what was non-terminal symbols, the parameters of the expression.
17:53:24
pjb
matzy_: a terminal symbol is a symbol that appears itself in the sentences (the expressions) of the language.
17:53:55
pjb
A non-terminal symbol doesn't appear in the sentences of the language, only in the grammar, to denote gramatical trees.
17:54:20
another-user
i have to go now, thank you all for great answers! i'll try to wrap my head around clos
17:54:36
pjb
So if you want, in the context of grammars, non-terminals are variables or parameters, while terminals are the literals.
17:56:25
another-user
beach: i'm really interested in your example, i hope i'll find you here at some time in future and i'll ask again
17:58:31
pjb
For example: function-definition ::= ( defun <name> ( <arguments> … ) <expression> … ) .
17:58:41
matzy_
i was taking that to mean that built-in cl things, like defun and when and whatnot, are terminal symbols
18:00:23
pjb
Now, since we can use destructuring-bind to decompose a sexp of that form into those elements, we could write: (destructuring-bind (defun <name> (&rest <arguments>) &body <expressions>) '(defun fact (x) (if (< x 0) 1 (* x (fact (- x 1))))) (list :name <name> :arguments <arguments> :body <expressions>)) #| --> (:name fact :arguments (x) :body ((if (< x 0) 1 (* x (fact (- x 1)))))) |#
18:03:43
pjb
It matches the corresponding parameters to the corresponding subexpressions of its second argument.
18:11:33
copec
I'm writing something in CL to manage ZFS Datasets across multiple platforms, so I am just invoking the ZFS CLI. Does there exist something where you provide a command and some form of a grammar to parse the return text and organize it into a type of object?
18:14:09
matzy_
I was doing this: (defmacro make-vars (&rest vars) `(progn (,mapcar (lambda (x) '(defvar x)) ,@vars)))
18:14:29
matzy_
i know it's not right yet but the mapcar was screwing me up. so it should be ,(mapcar ...)
18:14:54
pjb
(let ((list '(1 2 3))) `(mapcar (lambda (var) (+ 1 var)) ',list)) #| --> (mapcar (lambda (var) (+ 1 var)) '(1 2 3)) |#
18:16:17
White_Flame
that woudl at least build up s-expression nesting. From there, it's a toin coss whether applying it as a function/macro or destructuring it as data is more applicable
18:17:09
pjb
(eval (let ((list '(1 2 3))) `(mapcar (lambda (var) (+ 1 var)) ',list))) #| --> (2 3 4) |#
18:17:49
pjb
(let ((list '(1 2 3))) `(list 'the 'result 'is ,(mapcar (lambda (var) (+ 1 var)) list))) #| --> (list 'the 'result 'is (2 3 4)) |#
18:17:58
pjb
(let ((list '(1 2 3))) `(list 'the 'result 'is ',(mapcar (lambda (var) (+ 1 var)) list))) #| --> (list 'the 'result 'is '(2 3 4)) |#
18:18:05
pjb
(eval (let ((list '(1 2 3))) `(list 'the 'result 'is ',(mapcar (lambda (var) (+ 1 var)) list)))) #| --> (the result is (2 3 4)) |#
18:18:57
matzy_
what if you wanted four copies of (lambda (var) (+ 1 var)) with the list filled into each one
18:19:00
White_Flame
backquotes are basically shorthand for (list ...). So `(progn ,(mapcar ...)) is pretty much (list 'progn (mapcar ...)), so you can see that PROGN is a returned list form, and the mapcar runs in the parent (macro) context
18:21:55
matzy_
White_Flame: so i'm trying to use mapcar and a lambda to generate the lists of defvars
18:21:58
matzy_
(defmacro make-vars (&rest vars) `(progn ,(mapcar (lambda (x) '(defvar x)) ,@vars)))
18:22:05
pjb
Of course, you could also do that. It could even be more resistant to changes. But it could also break as easily as anything else upon changes in the format.
18:22:55
pjb
One advantage of writing down the grammar, is that it can easily be checked against the specification (a good specification would already have the grammar of its inputs and outputs written down, so it'd only be copy-and-paste).
18:23:40
pjb
And since we can generate the parser automatically, it gives the minimum of work to maintain your program in sync with the tool.
18:24:01
matzy_
White_Flame: i basically want everything in mapcar to be evaluated except that defvar
18:24:18
pjb
Also, the grammar will usually be complete, including the little details for data items that occur rarely, of which you may never see examples, and thus not implement otherwise.
18:24:56
pjb
(mapcar (lambda (line) (split-sequence #\space line :remove-empty-subseqs t)) (split-sequence #\newline zfs-output))
18:25:07
it3ration
hey folks, i was looking at cffi recently for CL and I was wondering how insane it'd be to write vulcan bindings
18:25:16
matzy_
i could map over my input list and assign it to a list containing the string "defvar" to be evaluated later
18:26:13
pjb
matzy_: if you want the string "defvar" you will get the string "defvar". But is it really what you want?
18:26:55
pjb
(mapcar (lambda (x) `(list "defvar" ',x)) '((+ 2 3) 4 5)) #| --> ((list #1="defvar" '(+ 2 3)) (list #1# '4) (list #1# '5)) |#
18:28:35
pjb
And anyways, you should never write defvar in the body of a macro or a function, since it won't do anything useful. defvar works at compilation time.
18:30:36
pjb
matzy_: (macroexpand-1 '(defvars *a* *b* *c*)) #| --> (progn (defvar *a*) (defvar *b*) (defvar *c*)) ; t |#
18:31:21
pjb
matzy_: so the macro defvars will take a variable number name of variables, as argument. How do you write that?
18:32:07
matzy_
(defmacro make-vars (&rest vars) `(progn ,(mapcar (lambda (x) `(defvar ,x)) ,@vars)))
18:32:10
pjb
So now, we have a variable named variables containing a list of variable names: (let ((variables '(*a* *b* *c*))) …)
18:32:48
pjb
matzy_: the only thick is that , will substitute the value, which is a list in this case.
18:33:08
pjb
But you want to substitite each element of the list instead. We say, to splice the list.
18:34:45
pjb
it3ration: ah, right. Well, perhaps you'd need more than just ffi, but yes, if you have a good C library defining the vulcan API, you can start by making a low level CFFI interface to it, and then write higher level tools in lisp.
18:36:05
pjb
matzy_: , doesn't care what the expression returns; an atom or a list it's the same for it.
18:37:44
matzy_
was reading the "write your own maco" section last night, and was confused so I came and found you awesome people :)
18:39:38
pjb
And as I said above, it has nothing to do with macros. See my examples, they don't involve a macro.
18:40:44
matzy_
so now i have: (defmacro make-vars (&rest vars) `(progn ,(mapcar (lambda (x) `(defvar ,@x)) ,@vars)))
18:41:33
jackdaniel
pjb: I remember only the conclusion, sorry :( I've discussed this problem here a year ago or something
18:41:34
pjb
matzy_: We would expect make-vars to be a function, not a macro. So to work only at run-time. While defvars would be expected to work at compilation-time.
18:42:05
pjb
Also, if you use abbreviations, don't use -. If you use -, use in-extenso names: define-variables or defvars.
18:43:00
pjb
something starting with def or define-, or with- or do or do- we would expect that to be macros.
18:44:09
pjb
matzy_: you need to count the backquotes and increment when you see one, and count the comma or comma-at and decrement when you see one. The variable references should be at the same level as their definition.
18:44:41
jackdaniel
or maybe it was that #1=(foo . #1#) can't be used in a macro, because compiler may try to expand it? anyway, nvm that
18:44:49
pjb
So in: (defmacro defvars (&rest vars 0 ) `( 1 progn , 0 (mapcar 0 (lambda (x) 0 `( 1 defvar ,@ 0 x) 0) ,@ -1!!! vars)))
18:46:31
matzy_
yeah i was so close and now i'm lost again. wouldn't doing @,(mapcar ...) do the expansion and substitution on everything inside that statement?
18:47:20
pjb
matzy_: (let ((vars '(*a* *b* *c*))) (mapcar (lambda (x) `(defvar ,x)) vars)) #| --> ((defvar *a*) (defvar *b*) (defvar *c*)) |#
18:48:04
pjb
We want to splice them in the progn: (let ((vars '(*a* *b* *c*))) `(progn ,@(mapcar (lambda (x) `(defvar ,x)) vars))) #| --> (progn (defvar *a*) (defvar *b*) (defvar *c*)) |#
18:48:23
pjb
matzy_: notice how I just write those expressions at the REPL to try them out immediately.
18:49:15
pjb
Then I can replace the let and bindings by defmacro and lamba-list: (defmacro defvars (&rest vars) `(progn ,@(mapcar (lambda (x) `(defvar ,x)) vars)))
18:50:25
pjb
And you can copy-and paste the literals and results to write the tests: (assert (equal (macroexpand-1 '(defvars *a* *b* *c*)) '(progn (defvar *a*) (defvar *b*) (defvar *c*)))) #| --> nil |#
18:53:29
pjb
(defmacro defvars (&rest vars) (let ((defvars (mapcar (lambda (x) `(defvar ,x)) vars))) `(progn ,@defvars)))
18:56:02
matzy_
so will it evaluate and splice the result and any thing inside of it that is not escaped?
18:59:17
pjb
matzy_: actually, I would argue that to understand, you would have to use the functions before using backquote.
18:59:36
pjb
(defmacro defvars (&rest vars) (let ((defvars (mapcar (lambda (x) `(defvar ,x)) vars))) (cons 'progn defvars)))
18:59:59
pjb
(macroexpand-1 '(defvars *a* *b* *c*)) #| --> (progn (defvar *a*) (defvar *b*) (defvar *c*)) ; t |#
19:00:19
pjb
compare with (defmacro defvars (&rest vars) (let ((defvars (mapcar (lambda (x) `(defvar ,x)) vars))) (list 'progn defvars)))
19:00:23
pjb
(macroexpand-1 '(defvars *a* *b* *c*)) #| --> (progn ((defvar *a*) (defvar *b*) (defvar *c*))) ; t |#
19:00:42
matzy_
basically if i have (let ((x 2) (y 3)) (+ x y)) in a macro, i would get (+ X Y) as my result, right?
19:01:03
matzy_
but if i do (let ((x 2) (y 3)) ,(+ x y)) - it evaluates everything inside the parens
19:05:34
pjb
matzy_: the exact rules are listed on http://www.lispworks.com/documentation/HyperSpec/Body/02_df.htm
19:06:16
pjb
matzy_: the exact rules are listed on http://www.lispworks.com/documentation/HyperSpec/Body/02_df.htm
19:08:22
matzy_
ok, i think i get it. ,@ works on the next form, which for me, is the entire mapcar form
19:08:44
matzy_
so does mapcar finish before ,@ is applied? or whats the order in a macro like this?
19:10:45
pjb
read again this example: (defmacro defvars (&rest vars) (let ((defvars (mapcar (lambda (x) `(defvar ,x)) vars))) (cons 'progn defvars)))
19:11:10
pjb
or this example: (defmacro defvars (&rest vars) (let ((defvars (mapcar (lambda (x) `(defvar ,x)) vars))) `(progn ,@defvars)))
19:13:27
pjb
By the way, is there any blind (or vision impaired) lisper? There's this hackernews question: https://news.ycombinator.com/item?id=18414562 ; I've commented suggesting lisp, but I'm not blind, so what do I know?
19:14:12
pjb
matzy_: a useful function for this kind of expressions is LIST* It's like CONS, but with several elements before the rest: (list* 1 2 3 '(4 5 6)) #| --> (1 2 3 4 5 6) |#
19:14:48
pjb
Of course, it can also be used with a single element: (list* 1 '(2 3)) #| --> (1 2 3) |# (cons 1 '(2 3)) #| --> (1 2 3) |#
19:15:45
pjb
To merge lists, use MERGE: (merge 'list '(1 3 5) '(2 4 6) (function <)) #| --> (1 2 3 4 5 6) |#
19:18:17
pjb
append doesn't copy the last argument. apply does like list*: (apply 'list 1 2 3 '(4 5 6)) #| --> (1 2 3 4 5 6) |#
19:18:52
matzy_
so your cons gets (cons progn (*a*) (*b*) (*c*)) at the end of macro execution, right?
19:19:38
jackdaniel
if anyone wonders what's going on with McCLIM, here are pixel-perfect bounding rectangles for a multiline text: http://i.imgur.com/1Ki3Uid.png :-)
19:19:55
pjb
you can use PRINT to see: (defmacro defvars (&rest vars) (let ((defvars (mapcar (lambda (x) `(defvar ,x)) vars))) (cons 'progn (print defvars))))
19:20:09
pjb
(macroexpand-1 '(defvars *a* *b* *c*)) #| ((defvar *a*) (defvar *b*) (defvar *c*)) --> (progn (defvar *a*) (defvar *b*) (defvar *c*)) ; t |#
19:21:08
pjb
(multiple-value-call #'+ (truncate 10 3)) #| --> 4 |# (multiple-value-call #'+ (print (truncate 10 3))) #| 3 --> 3 |#
19:27:23
pjb
Well, the function operator is the operator that creates closure, so it's the most important operator of lisp, I'd say :-) lambda is a macro that expands to (function (lambda …)).
19:28:58
LdBeth
pjb (IRC): even in (setq lexical-binding t), what (function foo) is still return 'foo
19:28:59
pjb
#+emacs (setf lexical-binding t) #+emacs (let ((x 42)) #'(lambda () x)) -> (closure ((x . 42) t) nil x)
19:29:57
pjb
#+emacs (let ((x 42)) (cl-flet ((foo (y) (+ x y))) #'foo)) --> (closure ((x . 42) t) (y) (+ x y))
19:30:34
pjb
of course, you are right for old emacsen and the legacy mode, so it makes emacs lisp rather complicated…
19:33:28
wusticality
pjb: i spose the tricky thing in general is memory management, I need to look into how that works with cffi
19:36:50
matzy_
pjb: ok i've been playing around with the solution you helped me come to in slime. so does @, mean in english"evaluate everything in the body AND splice the resulting list"?
19:38:01
matzy_
i took out the @ and saw the only difference is our result is wrapped in one extra list
19:38:35
anamorphic
LIST* has been quite handy when adding a new property to a plist: (list* :foo 123 '(:bar 456)) => (:FOO 123 :BAR 456)
19:39:46
White_Flame
matzy_: `(literal-term ,outer-term ,@outer-term) a term can be a symbol or a sexpr
19:49:33
matzy_
so if you have `(one two ,@(three four) five) -- three and four are the only things that are evaluated in that statement, right?
19:50:26
pjb
matzy_: this comes from the fact that there's no statement, only expressions and that expressions are lists, with a uniform format: the operator in the first position, the arguments in the rest).
19:51:54
matzy_
so the expression itself is evaluated, but that doesn't mean things in that expression will also be evaluated?
19:53:20
pjb
If it's a function, then yes, the arguments are evaluated, and the results are bound to the parameters of the function that is called.
19:54:28
pjb
(eval (let ((a 1)) `(+ ,@(+ 3 a)))) #| ERROR: The value 4 is not of the expected type list. |#
19:55:00
pjb
You cannot have dotted lists or circular lists in forms (programs). Only in data, as quoted literals.
19:56:37
pjb
but (let ((a 1)) `(+ ,@(+ 3 a) 4 5)) #| ERROR: The value 4 is not of the expected type list. |#
19:57:13
pjb
matzy_: it doesn't matter. You can just use variables: (let ((x 4)) `(+ ,@x)) #| --> (+ . 4) |#
19:58:16
matzy_
ok, but when i'm writing a macro, and it's backquoted, and i use a ,@ before my sexp, do i still need to force things in that sexp to eval or will ,@ take care of it? That's my entire question
20:00:13
pjb
The question is that you want to build a list. What kind of list do you want? What form should it have?
20:01:25
pjb
If you want a list of the form (+ <x> <y>) with <x> and <y> replaced by some expressions that you will compute and produce, then you can do it with LIST:
20:02:18
pjb
backquote which is a reader macro reads as an expression equivalent to my (list '+ <x> <y>):
20:05:13
pjb
matzy_: finally, you would use ` if you can write a model of the form of the list you want. And you would use list, list*, cons, append, etc if you are building a more abstract data structure.
20:05:52
pjb
But even considering that, often it will be clearer to use the list operators, possibly wrapping them in functional abstractions.
20:06:21
matzy_
so back to our solution real quick: (defmacro defvars1 (&rest vars) `(progn ,@(mapcar (lambda (x) `(defvar ,x)) vars))) -- why is lambda not just treated as a symbol, like we do for progn?
20:07:12
pjb
For example, you could write: (defun toplevel-expressions (expressions) (list* 'progn expressions)) (defun make-defvar (var) (list 'defvar var)) (toplevel-expressions (mapcar #'make-defvar '(*a* *b* *c*))) #| --> (progn (defvar *a*) (defvar *b*) (defvar *c*)) |#
20:07:48
pjb
(defmacro defvars (&rest vars) (toplevel-expressions (mapcar #'make-defvar vars))) ; is way clearer!
20:08:40
matzy_
but that was my question i've been wondering about - why is lambda evaluated in that backquoted sexp?
20:09:05
pjb
The only detail is that you need to have the functions toplevel-expressions and make-defvar defined in the compilation environment, since they're used at macroexpansion time. So if you define them in the same file as a the macro, you need to put them in an (eval-when (:compile-toplevel :load-toplevel :execute) (defun …) …) form.
20:10:46
matzy_
got it! i didnt know if ,@ was one symbol that acted on the whole sexp or if , applied to each thing in the sexp and @ affects the resulting list?
20:11:28
pjb
Well, technically it's not a symbol, it's pure syntax, parsed and interpreted by the ` reader macro, but yes. , and ,@ are two different things.
20:12:11
pjb
`(let ((x '(1 2 3))) `(a ,@ x b)) #| --> (a 1 2 3 b) |# `(let ((@ '(1 2 3))) `(a , @ b)) #| --> (a (1 2 3) b) |# ;-)
20:12:43
matzy_
if i need to escape backquotes will @, do that and splice or does it just splice? seems to be the former
20:13:55
matzy_
it's like in my macro, if i didnt have @, there at all the lambda wouldn't be evaluated, right?
20:14:56
matzy_
if i just use , it evaluates everything in the sexp. if i use ,@ it evaluates everything in the sexp, splices the resulting list, and inserts it back into the resulting list
20:15:04
pjb
`(progn (mapcar (lambda (x) `(defvar ,x)) vars)) #| --> (progn (mapcar (lambda (x) (list* 'defvar (list x))) vars)) |#
20:15:48
pjb
Well, more precisely, ` builds an expression where those expressions will be evaluated.
20:17:02
pjb
So you can see that x and xs are not evaluated. But they're inserted in the resulting expressions in such a way that if you evaluate this resulting expression, they will be evaluated.
20:17:20
pjb
(let ((x 42) (xs '( 4 2))) (list* 'foo (list* x (append xs '(bar)))) ) #| --> (foo 42 4 2 bar) |#
20:18:07
pjb
matzy_: indeed, we have to be careful and distinguish read-time, compiltion-time, macroexpansion-time, and run-time and sometimes several instances of them embedded.
20:19:19
pjb
So if you don't evaluate the backquoted expression, then the subexpressions are not evaluated.
20:20:24
pjb
But: (quote `(1 (+ 1 2) ,(+ 1 2) #.(+ 1 2) 2)) #| --> (list* 1 (list* '(+ 1 2) (list* (+ 1 2) '(3 2)))) |#
20:20:52
pjb
,(+ 1 2) implies that (+ 1 2) will be evaluated at run-time, while #.(+ 1 2) evaluates (+ 1 2) at read time; it's like if we read 3.
20:21:35
pjb
matzy_: notice you can prevent the expressions read by #. to be evaluated by setting *read-eval* to nil.
20:22:25
pjb
(setf *read-eval* nil) (quote (#.(+ 1 2) 3)) --> Reader error on #<string-input-stream #x30200236649D>, near position 33, within "(quote (#.(+ 1 2) 3)": #. reader macro invoked when *read-eval* is false .
20:23:17
pjb
But be careful with C-x C-e, since this evaluates the expressions in a separate REPL, those variables must be set globally in the slime-repl, before they can be taken into account in the threads used by C-x C-e.
20:24:08
pjb
alternatively, you can set swank::*swank-bindings* and also swank:*default-worker-thread-bindings* SWANK:*MACROEXPAND-PRINTER-BINDINGS* SWANK::*INSPECTOR-VERBOSE-PRINTER-BINDINGS* SWANK::*INSPECTOR-PRINTER-BINDINGS* swank::*backtrace-printer-bindings*
20:26:11
pjb
So you can use it to generate some code or some data when you program. In effect, this adds an edit-time to the various lisp times :-)
20:33:07
pjb
matzy_: yes, C-x C-e displays the result in the minibuffer. C-u C-x C-e inserts it in the buffer.
20:33:28
pjb
matzy_: C-u is a prefix modifier to emacs commands. A lot of commands can perform variants with it.
20:34:26
matzy_
but i use evil, so i feel like there are a lot of keybindings i'm missing out on that dont get ported
20:42:30
matzy_
but do you ever really think about which vim state you're in? do you think you're in "highlighting mode" when you drag your mouse across text while holding down left click?
20:43:08
matzy_
it's just natural, when you want to type you go in insert and leave immediately after. i've never had to really think about it, since my first week probably (been awhile now)
20:43:52
matzy_
plus i dont see how people live without text objects - ci(, da', vi{......i use those more than any other command
20:46:02
matzy_
i agree, but something that's semantic (like vim's text-objs and motions) comes pretty damn close (if you speak english)
20:46:48
esper0s
i do agree that vi had it pretty well down when it comes to semantically differentiating between text
20:48:52
matzy_
esper0s: does it? i'm curious, i never got beyond the motion keys in the tutorial because i found them so ridiculous
20:59:36
fiddlerwoaroof
matzy_: I use a weird mix of vim and emacs keybindings now, things like C-M-k for (delete a sexp) are particularly useful.
21:02:48
esper0s
have you guys heard of the book "Structure and interpretation of Computer programs" written by MIT and using lisp