libera/#commonlisp - IRC Chatlog
Search
4:55:52
beach
shawnw: This: https://github.com/s-expressionists/Consecution implementation of the sequence functions is based on generic functions.
9:54:52
dnhester26
am having a hard time understanding why everyone (AKA Paul Graham in the book ANSI CL, and this https://github.com/ruricolist/spinneret ) use macros to generate HTML. Why not just use functions and objects? I've yet to see one case where I understand that something is more convenient in macros, actually the other way around. What am I missing?
9:55:37
jackdaniel
.some more concurrent drawing: https://turtleware.eu/static/paste/concurrent-drawing.mp4
9:58:44
dnhester26
Even in the docs of spinneret, the "great advantage" he says does only make sense if you are printing to a stream from the function. If instead each function generates an object, there is no problem of print order
9:59:21
dnhester26
ok, that's specific to their docs, I just wanted to know in general why that's a good use case for a macro since I don't see any benefits at all
10:08:54
dnhester26
beach: a cool feature I saw yesterday which to me was very impressive: I was editing some code which has multiple sequential format forms expressions in it, the first format has an open parenthesis and the last format was printing a close parenthesis. When my cursor was over the opening parenthesis inside the string, it highlighted the closing parenthesis a few expressions later! Even though the were a bunch of other parenthesis in
10:08:54
dnhester26
between from the actual common lisp code, it somehow picked up to highlight the closing parenthesis of the string, even though it was in an expression separate from the first one. I was blown away by that
10:11:02
beach
It probably too the entire thing to be an expression, and what was a closing double quote followed by an opening double quote, it took to be the inverse.
10:13:21
dnhester26
right, the strings where in different expressions adjacent to each other, meaning each one was the argument to a format expression
10:14:41
beach
No, I meant, it perhaps took "... ( ... " ... "... ) ... " to be ... ( ... " ..." ... ) ... so a single expression with literal strings in it.
10:32:57
dnhester26
hm, the code looked like this (format str "( hello world") \n;;some comments\n (format str "random text") (format str "some other text )") and the cursor would highlight correctly the parenthesis of the CL expressions, and then even the parenthesis inside the strings
10:41:59
gilberth
dnhester26: I never got why people won't just generate HTML as a tree (s-expr would be fine actually) first, to then finally dump it. And as I see this silly macro approach used again and again, I still feel like I'm in the minority.
10:44:06
gilberth
A functional approach has many advantages. First of all my order of evaluation doesn't need to map the order of HTML output. I can generate HTML fragments in any order, cache them, rearrange them, filter them, duplicate them. Just I would manipulate s-expressions.
10:46:07
dnhester26
gilberth: ah so you also see it like me? In the ANSI CL book it's given as *the* example for macros, and I just thought that every tag can be a function which instantiates an html-tag object and every function basically takes in &rest rest as parameters, and inside the function we just basically split it into two lists, one a property list to use it for the properties of the tag, and the other are the children of the tag. Finally on
10:46:07
dnhester26
e function render just takes in an html-tag object and does depth-first navigation passing it to some printer
10:47:12
dnhester26
isn't everything an s-expression? what's the difference between functional vs s-expression, I didn't follow what you meant
10:49:14
gilberth
I'm lazy. I have a HTML internal representation like (:P "See " (:A :HREF "http://example.com" "here") " for further information.") Then I can just use backquote to construct those documents, if lazy. But I moved away from that and would have a function called "$" to make HTML nodes. Like ($ :p "See " ($ :a :href ...) ...) and another called @ for splicing.
10:50:08
gilberth
I meant s-expressions in contrast to proper objects. Like some DOM-ELEMENT, DOM-TEXT, etc classes or so.
12:50:33
beach
dnhester26: I think what gilberth means is that he uses functions that create a tree consisting of CONS cells and atoms representing the HTML tree, and then he has a single function in the end that turns that tree into HTML.
12:52:33
beach
dnhester26: And you are almost right, in that every Common Lisp datum is an S-expression, but "everyTHING" is not an S-expression, like a comment for instance.
14:06:04
dnhester26
Bubblegumdrop: yeah, but everyone i've heard, including paul graham himself, says macros should be avoided unless absolutely necessary
14:23:23
Bubblegumdrop
there's nothing stopping you from writing your whole program using defmacro lol
14:25:47
beach
So then, you really meant that things could get messy only if you name your macros inappropriately, yes?
14:27:02
beach
And even then, I don't see a problem, provided that the package exporting that symbol (packages export symbols, not macros) is not :USEd.
14:27:24
Bubblegumdrop
none of this is documented anywhere afaik, is there a "good macro use" section in the manual?
14:28:27
beach
Macros are used only when the syntax (at the S-expression level) of the language must be extended.
14:30:31
jackdaniel
they are surely used also when a normal function would do; common mistakes also constitue usage ,)
14:31:03
jackdaniel
but generally macros are a useful tool and symbols denoting them are often exported
14:31:35
jackdaniel
there's some general issue with macros capturing the package for generated symbols (if they introduce new symbols)
14:32:16
Bubblegumdrop
beach like dnhester26 says the general concensus seems to be "macros should be avoided" but I don't really know too much documentation/reference as to why except through trial and error and reading the writing of others on issues close to the matter
14:32:44
jackdaniel
macros should be avoided by people who don't know the language, because they are often abused due to not understanding what they are good for
14:33:07
Bubblegumdrop
I think I have enough lisp experience to know when I don't understand what I'm looking for :P
14:35:21
dnhester26
jackdaniel: is this what you are referring to? see line https://codeberg.org/kilianmh/openapi-generator/src/branch/dev/code/function-generation.lisp#L487 487, and here the code generated https://plaster.tymoon.eu/view/4646#4646 line 9
14:35:25
beach
Bubblegumdrop: Like I said, macros should be used only when the syntax needs to be extended (in other words, when existing syntax won't be enough) for the abstraction you want to create.
14:36:00
dnhester26
it somehow keeps the package of where the macro is defined `openapi-generator::output` but I would like it to be in the package where the macro was executed
14:36:40
dnhester26
that project basically generates a new system and code, not sure if he is using macros instead of printing for convenience
14:37:16
beach
dnhester26: Symbols are created by the reader, so you can't (easily) change the package of a symbol like what you want.
14:37:18
dnhester26
I wanted to ask, it's basically just a let inside a macro, but when it's expanded inside some other package, the let variable is prefixed with the macro's package
14:37:59
dnhester26
I don't even follow why that would be, since it's in a let definition, why is it part of any package to begin with if the whole idea of a let (to my poor understanding) is to have a temporal variable inside the let and not outside
14:38:42
beach
dnhester26: Like I said, symbols are created when the code is read by READ. It is not that "the variable is prefixed", it is that the symbol representing the variable has the "macro package" as its home package. Whether it is prefixed or not, depends on the current package.
14:39:16
dnhester26
beach: i need to read about the reader, I only have the very basic idea from the intro books that I've read which is what eval uses to understand the code, so I don't really follow why that would be
14:40:26
beach
dnhester26: When the code is read by READ, and the reader sees the characters (say) v a r i a b l e, it creates the symbol named VARIABLE in the current package, which is the package in which the macro is defined.
14:41:18
beach
dnhester26: Every sequence of characters that looks like a symbol name is interned in some package by the reader, and if it does not have a package prefix, then it is interned in the current package.
14:41:45
beach
dnhester26: The reader doesn't care whether the symbol is inside a LET or somewhere else.
14:41:47
dnhester26
I guess what you are saying makes sense since auto complete on those let variables only ever works for me in emacs after compiling a function, but I'm surprised that the symbol is created. I guess the data in the symbol is only associated during the execution of the let
14:43:24
beach
dnhester26: The reader is responsible for turning a sequence of characters into an S-expression represented as a tree of CONS cells and atoms. How would that look if the symbol were not created at all?
14:43:36
dnhester26
when making a macro that says would generate some function, and inside the function there will be a let form naming an "age" variable, is there any best practice for making that symbol in the macro package vs the package where the macro is called?
14:44:20
beach
dnhester26: The symbol is created by READ, so there is nothing that can be done about that after the code has been read and compiled.
14:45:21
beach
dnhester26: READ is no longer used after the code has been read once and then compiled.
14:46:42
dnhester26
beach: are you taking about the macro itself? the macro is compiled in it's package once it's read? i thought if we did intern it will create the symbol in the package where the macro is being executed
14:48:47
beach
Besides, why do you care? LET (usually) creates a lexical variable, and its name is not important.
14:57:09
dnhester26
ok, so it's not really important. In the case of that particular system, it's using a macro to just generate files for a new system, so I think it's basically like printing using macroexpand, so if the package is the package of the macro, it means that the new system must have that macro's system as a dependency instead of generating a stand alone system
14:58:58
scymtym
the fact that emacs with slime does not autocomplete names from code that has not been read is actually a good example of a limitation that is hard to lift within emacs/slime and that matters in pratice (regarding the discussion a few days ago)
14:59:31
beach
dnhester26: I mean, sure, if a program wants to use a macro from some system (say S), then that program must depend on system S.
15:02:43
dnhester26
beach: the use case is user loads system A in REPL, calls a function generate on package A defined in system A, (a:generate "my-new-system"). Then, this will write files in an output folder creating a a new system. The way he is generating the text in the files of the output is by using macros, and in one of the generated files, inside a let, one of the variables in the let are named a:output. So now, if there is no package `a` defi
15:04:07
beach
Yes, I see. That seems like a strange thing to do though. Not that I know how to fix it.
15:04:59
beach
dnhester26: I guess it is not a very good idea for a macro to introduce symbols like that. It should use GENSYM.
15:10:32
dnhester26
i submitted an issue and tried patching it according to how the code works, but then I started reading more on gensym and interning and got confused because it's different than how I understood it...
15:11:35
beach
Well, a first read reveals the usual stuff I point out, so I am not excited about repeating all that.
15:20:30
jackdaniel
I think that scymtym mentioned that #',foo may be not conforming (given the reader specification)
15:26:55
beach
dnhester26: WHEN and UNLESS should be used only in situations where the value is not needed, like a form other than the last in a PROGN for instance.
15:28:29
beach
dnhester26: The standard explains how to use different "versions" of NIL. In a LET, '() means the empty list, NIL means either the Boolean value FALSE or a default value. 'NIL means the symbol NIL.
15:29:11
beach
dnhester26: I am not talking semantics here. I am talking communicating with the person reading your code. Like elementary software engineering.
15:30:17
dnhester26
ah ok, so if I actually want to use the NIL value if the when is false it's better to use `(if cond (expr) NIL)`?
15:30:24
beach
dnhester26: WHEN and UNLESS indicate to the person reading the code that some side effect is to be performed conditionally, and that the value is not used.
15:31:00
beach
dnhester26: Yes, like I said, when the value is used, an IF with both branches should be used.
15:31:20
dnhester26
ah I didn't know that. So use `if` when I am interested in using the returned value of the conditional expression, and when and unless when I don't care about the return value
15:32:21
beach
WHEN and UNLESS if the context is such that the value is not used, and of course, that there is only a `then' or an `else' branch and not both.
15:33:27
dnhester26
https://google.github.io/styleguide/lispguide.xml?showone=Conditional_Expressions#Conditional_Expressions
15:36:36
beach
Also the use of AND and OR for things other than Booleans is a violation of the rules stated on page 13 of the LUV slided. And I repeat that one regularly as well.
15:39:25
jackdaniel
1993 guide to common lisp style; I think that the first sentence in What To Believe slide is the most important advice covered there
15:42:56
beach
::notify gilberth The "streams" dictionary of the NovaSpec seems messed up in that some functions have their parameters explained, followed by ", Function".
15:44:27
beach
dnhester26: The main thing to keep in mind is that the code you write is meant mainly to be read by other people, and you want to assist those people, so as not to waste their time, by giving signals (beyond semantics) that indicate the purpose of the code.
15:45:51
beach
dnhester26: So if you do (LET ((X NIL)) ... (APPEND X ...)) you are first telling the person reading your code that x is either a Boolean or a default value, and then when the APPEND is seen, the reader is confused because you are using X as a list.
15:46:10
dnhester26
why is using a cond better than if and progn, because all the forms in a cond can be added inside the parenthesis without the need to explicitly write progn?
15:46:40
beach
dnhester26: By doing (LET ((X '())) ...) instead, you help the person reading your code by indicating that X is a list.
15:46:52
dnhester26
yeah, that's clear. But in the case of when I thought it's just like if with one branch
15:48:09
beach
dnhester26: Well, horizontal space is a precious resource. A COND uses less horizontal space than an IF with PROGN (or more vertical space if you add newlines, also a precious resource) so a COND is often preferable.
15:48:45
beach
dnhester26: But I use IF with PROGN if I have two branches and horizontal space is not a problem.
15:48:45
dnhester26
what about the when, why is it not just an if with one branch which is then shorter to write?
15:49:11
beach
dnhester26: Short to write is not an objective. The objective is to give signals to the person reading your code.
15:50:04
beach
dnhester26: And and IF with a single branch should never be used. If it is in a value context, then both branches should be used. If not, WHEN or UNLESS should be used instead.
15:50:28
dnhester26
so it's because it's a convention to use when for side effects instead of the value they produce, but not because the correctness of the usage is in question
15:52:36
beach
dnhester26: Again, it is not about semantics, but about assisting the person reading your code.
15:53:29
beach
... so "correctness" is ambiguous here. It is semantically correct, but not correct from the point of view of elementary software engineering.
15:54:24
beach
dnhester26: Surely, this can't be the first time you hear about helping the person reading your code by following conventions and such.
15:56:10
dnhester26
do they speak about the usage of when and unless as you've described? or where should one go about learning the convention if reading code in the while regularly violates it
15:56:12
beach
dnhester26: You will get a lot of protests from people here, but I find the rules very good myself.
15:57:54
beach
dnhester26: If I see something like (LET ((THINGY (FIND-THINGY ...))) (WHEN THINGY ...)), I have to pause for several seconds and mentally transform the WHEN to (UNLESS (NULL THINGY) ...)
15:58:36
beach
So I subscribe to the restriction that WHEN, UNLESS, AND, OR should be given Boolean expressions.
15:59:38
beach
Many of the protests can of course be explained by the fact that the person writing the code does not care very much about other reading it, and, as you pointed out, it is shorter to write.
16:00:32
dnhester26
ah, I always think of (when thing) as does thing have a value, then do something, but maybe that's only because I've been using it that way for a while now
16:03:00
beach
The way I see it (and I have said this also many times) is that FIND-THING returns either a THING or some default value. The best way to write it then is (LET ((THING (FIND-THING ..))) (WHEN (THINGP THING) ...))
16:03:35
beach
Then you are independent of what that default value is, and you can freely change it in the future.
16:05:04
beach
dnhester26: You will get a lot of protests from people here, but I find the rules very good myself.
16:05:38
beach
The "NIL as a default value" can be seen as an implementation detail being propagated in the code.
16:07:28
beach
dnhester26: And, again, jackdaniel is right, of course. Rules must be followed or not, depending on context. The main takeaway is that you want to assist the person reading your code.
16:17:36
beach
An amusing thing about those (usually newbies) who protest against many of the rules is that they say they don't care much about people reading their code. But then they go right ahead and expose their code here and ask for help. And they resent being told by the person trying to help out that they could do better to assist that person.
16:42:14
Bubblegumdrop
dnhester26 this openapi-generator is somebody elses project? It's under active development? I'm trying to use it with https://github.com/oobabooga/text-generation-webui/ do you only use it with OpenAI? It looks like it supports any OpenAPI
16:43:27
dnhester26
Bubblegumdrop: yeah, it's meant for OpenAI, read his readme, he has some goals and features to do next
16:43:48
dnhester26
It's convenient to make a quick client for an OpenAPI like they have in other languages
16:45:36
dnhester26
I only learned about this this past couple of weeks because I have to integrate with some APIs and they document it that way and apparently it's very convenient for generating a client application automatically in a bunch of languages. It looks like he did one for CL which is helpful to avoid the manual labor of transcribing the api into functions in a client
16:46:22
Bubblegumdrop
However I'm having trouble getting it to load. It seems to choke without authorization tokens. Working my way through it now.
16:47:33
dnhester26
hm? you should only need the url to a json/yaml file and the project itself and that's it
16:47:46
Bubblegumdrop
#<common-lisp:standard-generic-function openapi-generator::parameter-schema-type (1)>
16:47:52
Bubblegumdrop
((#<openapi-generator::parameter name: authorization, in header {101F1F98D3}>)).