libera/#commonlisp - IRC Chatlog
Search
23:55:49
aeth
the way lists go, a push is a cons, so how can it not do that? there is no list, only cons pairs
23:56:36
aeth
pushes could only directly modify if the list data structure was actually one more pointer, a pointer to the first cons pair (or if it added to the end and was O(n))
23:57:47
moon-child
it could also add to the beginning destructively, moving down the elements of each successive cons cell. Still O(n) though
0:07:54
aeth
if I have a (with-open-file (stream #P"test42.txt" :direction :output :if-exists :append :if-does-not-exist :create) ...) for quite a few streams (42 would not be too many, since I'm talking about IRC logging), when do I hit limits?
0:09:17
moon-child
on modern unix, a file descriptor is a signed 32-bit integer between 0 and 2^31-1 or so
0:09:47
White_Flame
there are limits beyond running out of descriptors though. Don't recall where they are unfortunately
0:11:19
aeth
Very naive is just leave all of the streams open for all of the files and just keep appending, with periodic FORCE-OUTPUT. Less naive is to only leave it open for very active channels (e.g. here), while not leaving it open for very inactive channels (e.g. my own project channels) until necessary.
0:11:33
aeth
However, guessing "inactive" incorrectly is going to mean opening/closing a lot of files
0:12:03
aeth
I suppose it kind of has to guess it, though, once queries are taken into account, but those can just be special cased to time out, unlike channels, which are probably going to have some joins/parts/quits/nicks at the very least
0:12:59
aeth
I could also buffer it for an arbitrary amount of time, but the longer the buffer period is, the more likely that a system failure loses a lot of logs.
0:14:53
aeth
I guess with-open-file isn't the way to go since (1) there will be a lot of files and (2) files need to be able to "time out" if inactive until active again (at the very least, for queries)
0:43:24
pjb
aeth: note: the programs use already a good number of the available file descriptors for the shared libraries etc.
0:48:13
aeth
you can "easily" be in a few hundred channels (I'm constantly in anywhere from 16 to 35), which is already pushing into the limits assuming that the machine isn't being used solely for IRC
0:49:18
aeth
And yet, every single channel might be active if it logs joins/parts/quits. Most trivially, if you ping out and reconnect before your connection gets the ping timeout, since that ping out will show up in every channel you (re)join.
0:51:54
phadthai
shared lib fd are usually closed after reading+mapping, for irc there's the standard 3 (stdin/stdout/stderr) plus one socket per network/server, possibly a few for the display via curses etc... but yes if every channel is logged depending on client it may also mean one fd per channel. As for the 1024, it's probably the process limit more than the total system one
0:52:59
moon-child
you can split up your thing into a bunch of different processes, where each one only has a file or two open
0:54:18
aeth
White_Flame: for one thing, idk the no-power failure state of sqlite, while I know the no-power failure state of appending to a bunch of log files
0:55:22
aeth
also, IRC is pretty much designed for appending to plain text files since it's a text-and-line oriented protocol with no editing/deletion
0:55:53
White_Flame
quassel logs everything to sqlite, and I've not had a problem with recovering from system crashes and such
0:56:03
aeth
Afaik, some people lost their logs when they moved from freenode to libera because it wasn't just plain text files.
0:57:17
aeth
you'd only need a database to cache unsent messages in a bouncer (not client)... more for the reliability guarantees than anything else
0:57:43
White_Flame
also, since irc logs are often-written, seldom-read, you could just do a singular linear log, and grep for individual channels later
0:58:59
aeth
White_Flame: I mean, yes, that's what I do to display my bot's messages in SLIME (it's running in a background thread).
0:59:58
aeth
White_Flame: However, the stateless all-in-one-place log has a few issues. In particular, QUIT and NICK (and perhaps a few others) are global in IRC, which makes it unclear in which channels those are actually taking place.
1:01:56
aeth
Also it becomes unreadable without grep if I was in a bunch of channels, with one way noisier than the others. For instance, my SLIME is basically just a log of #lispcafe because cond is present there, but it is also present in other, quieter channels
1:11:16
waleee
what would be the likely reason for someones (not me) M-x slime-connect to fail with "Can't locate module: SWANK-IO-PACKAGE::SWANK-INDENTATION"?
3:22:34
lisp123
I want to add keys to slot-a in class B but not class A, i.e. in B it should look like (slot-a :accessor :get-slot-a :initform nil :MY-CUSTOM-KEY key-value) whereas in Class A it should be the same but without my-custom-key
3:24:01
lisp123
loke[m]: yes exactly, class b has a metaclass, and class a is the generic form of it without a metaclass
3:26:28
beach
lisp123: Your example is very strange with a GET- prefix and an access that is named with a keyword.
3:28:11
lisp123
beach - I did get instead of b-slot-a since I want the same accessor function across the two classes
3:29:25
lisp123
I want to use BKNR datastore to store objects (it uses a metaclass + requires some keys) but want a generic version of the class to also play around with when I don't want it ta automatically go to the datastore
3:29:26
loke[m]
lisp123: You are aware of the fact that accessor functions are independent from classes, right?
3:30:11
lisp123
yes, but if I name the accessor function class-name-slot-name, then it wouldn't work for 2 classes, since they have different class names
3:31:05
beach
lisp123: The name of the access or totally unrelated to the class names. You can name it whatever you want.
3:35:39
beach
Well, I haven't thought of a solution to your problem, but 1. The prefix GET- seems like an import from languages that don't have SETF and should be avoided. 2. The accessor should be named after some intrinsic property that you want to read or write, whereas a slot is an implementation detail the name of which is usually uninteresting.
3:42:18
beach
So, do I understand you correctly that you want to use the custom initarg in a slot option when you define class B using DEFCLASS, but you don't want it to be possible when you define class A using DEFCLASS?
3:43:49
lisp123
Yes (p.s. 'slot' in 'slot-name' was just for this example, now that I read your comment a second time - I don't use the word slot in my slot names. I haven't used GET so far, but having a think now on how to avoid it as much as possible)
3:44:38
lisp123
Also, Class B is a metaclass of bknr.datastore:persistent-class - whereas Class A is not - that's the main distinction
3:45:27
beach
Well, example: In Common Lisp it is more convenient to say (name <instance>) and (setf (name <instance>) <value>).
3:47:06
lisp123
Yeah I do the same style as you do it, but it's not a super strong view I have. For example, with database related functions I'm currently liking adding the verbs 'insert' 'create' etc in because it distinguishes them to some degree from a vanilla CL form
3:51:31
beach
lisp123: My immediate reaction is that you may need to intervene in some MOP stuff like COMPUTE-EFFECTIVE-SLOT-DEFINITION.
3:53:42
beach
Your direct slot definition classes are different in A and B since A does not have a special metaclass, but B does. So the metaclass of B has a different DIRECT-SLOT-DEFINITION-CLASS.
3:55:28
beach
So I presume COMPUTE-EFFECTIVE-SLOT-DEFINITION is defined by the bknr library so that it can handle the custom DIRECT-SLOT-DEFINITION-CLASS, and it may very well ask for the value of a slot in the direct slot-definition object that is not present in the direct slot from A.
5:53:29
loke[m]
lisp123: Do you know what when you specify :READER (or :ACCESSOR or :WRITER) all that really does is to create a DEFMETHOD on that generic function?
5:54:11
loke[m]
The magic in accessors are actually in SLOT-VALUE (and the more specific SLOT-VALUE-USING-CLASS)
6:38:47
coat
are lists passed by value into functions? when I push new elements to a list within a function, the caller of the function cannot see those changes.
6:39:42
spiaggia
Common Lisp uses call-by-value, but the values are semantically speaking references to objects in memory.
6:40:10
moon-child
all objects are references, but they are passed by value; 'push' reassigns a local value
6:40:47
spiaggia
coat: A list is either NIL (denoting the empty list) or a CONS cell. Can I assume you knew that?
6:41:15
spiaggia
coat: I.e., a Common Lisp list is not an abstract data type, or not so much. It's implementation is known.
6:41:57
spiaggia
So when you pass a list to a function, you pass a reference to the first CONS cell. The caller and the callee share this reference.
6:42:01
coat
"push reassigns a local value" - did not know about this. But I see it is mentioned in CLHS in the notes. "The effect of (push item place) is equivalent to (setf place (cons item place))" I had overlooked this note earlier
6:42:36
spiaggia
coat: PUSH changes a "place". When the place is a variable, it changes the value of that variable.
6:43:59
spiaggia
coat: Think of (pushd <object> <variable>) as roughly equivalent to (setf <variable> (cons <object> <variable>))
6:44:47
moon-child
nit: 'has no effect on the caller' unless it closed over the caller's environment
6:45:13
coat
spiaggia: Yes, CLHS notes for push has this: " The effect of (push item place) is equivalent to
6:45:16
coat
except that the subforms of place are evaluated only once, and item is evaluated before place. "
6:45:16
spiaggia
coat: This is the same reason why your NCONC didn't work when the first list is empty.
6:46:14
spiaggia
coat: NCONC is a function, so when you pass it NIL as the first argument, there is no last CONS cell to update, so it just returns the second argument and does not update anything.
6:47:26
coat
in an earlier discussion, push was discouraged because it mutates the list but it does not, does it? For example: (defparameter *a* (list 10 20 30)) (defparameter *b* *a*) (push 40 *a*) Here *b* is unaffected
6:47:37
spiaggia
So to use terms from general programming, the LIST type in Common Lisp is not an abstract data type, and PUSH is therefore not an operation on instances of an abstract data type.
6:48:41
spiaggia
coat: PUSH will not mutate the list if the place is a variable. But if the place is (cdr (last ...)) then it will.
6:49:59
pjb
if the place is var (and var is not a symbol-macro to something else), the it mutates the variable var.
7:05:04
coat
Please help with this: https://plaster.tymoon.eu/view/2515 - I am unable to understand the macro expansion.
7:06:46
coat
why does PUSH behave differently when called outside a macro and when called inside a macro?
7:08:43
White_Flame
I don't know why it's not erroring out completely, which implementation are you using?
7:09:41
coat
White_Flame: SBCL. It is not erroring out I guess because I have not called the macro yet. The macro is of course incorrect. I am just trying to understand the expansion of my incorrect macro.
7:10:18
coat
What does it mean to push to a symbol? This is not valid code, is it? (push (cons "a" "apple") '*a*) ; This errors out
7:10:48
White_Flame
because you're calling PUSH on the literal source code passed in, instead of generating the runtime code to perform teh push
7:11:35
White_Flame
I bet it has to do with either compiler macros or general compiletime optimization of PUSH that shuffles things around in this way
7:12:49
coat
how does (push-key-value "a" "apple" *a*) end up expanding to (("a" . "apple") . *A* I want to understand in a step by step manner.
7:15:14
White_Flame
the lambda body's PUSH expands to (LET* ((#:ITEM (CONS "a" "apple"))) (SETQ Y (CONS #:ITEM Y)))
7:16:40
White_Flame
in any case, do not compute anything at compiletime that requires macro parameter values
7:21:56
White_Flame
you could consider the macro you wrote to basically be doing (let ((a '*a*)) (push <something> a))
7:22:30
White_Flame
which of course just pushes onto the macro function's local variable A, which happens to be passed the symbol *A* in that invocation
7:24:34
beach
coat: I created this page a while ago for newbies like you: http://metamodular.com/common-lisp-semantics.html
7:24:59
White_Flame
(I tend to assume non-newbie in #commonlisp, and newbie in #clschool I guess :-P)
7:26:14
White_Flame
but still, stepping back and considering that you're not actually doing any macro code generation, the body is just like a function and is PUSHing to its own variable A
7:26:49
coat
White_Flame: yes, I understand that. My macro is wrong. But I wanted to understand its wrongness precisely before moving on
7:28:30
coat
noticed a strange thing in my Emacs. Both C-c ENTER and C-c C-m does slime-expand-1. But when I type C-h w slime-expand-1 RET it says slime-expand-1 is on C-c RET. Why does it not know that it is also on C-c C-m?
7:30:16
White_Flame
(of course, on second thought I guess ascii control codes and such might not be as much common knowledge anymore)
7:30:18
coat
I picked up C-c C-m from this doc: https://common-lisp.net/project/slime/doc/html/Macro_002dexpansion.html - Never stopped for a second and thought about it to realize that C-m is equivalent to RET.
7:31:40
White_Flame
I just happened to learn it as C-c RET, from whatever source I originally saw it
7:37:55
susam
coat: Another trivia: 'M' = 77 (dec) = 4d (hex) = 100 1101 (bin). The ctrl + char combination zeroes bits 6 and 7 (first two in this example) of char. We get 000 1101 (bin) = 0d (hex) = 13 (dec) = RET.
7:39:17
moon-child
I always thought of ctrl as masking the low 5 bits. But it occurs to me that with an 8-bit meta it couldn't do that; it would have to, as you say, zero just bits 6 and 7
7:39:30
coat
susam: wow! never knew ctrl is meant for zeroing bits of character code. I always thought ^A = 0, ^B = 1, etc.
7:41:15
susam
coat: Well, yes. Different ways to look at the same thing. '@' = 0x40, 'A' = 0x41, etc. So zeroing bits 6 and 7 is equivalent to selecting the lowest 5 bits with a mask which is equivalent to ^@ = 0, ^A = 1, etc.
7:41:57
White_Flame
in any case, an ascii table clearly shows all the alignments between case, ctrl codes, etc