libera/#commonlisp - IRC Chatlog
Search
3:10:37
Josh_2
I have thousands of SLOC that rely on plists, and now I have to convert it all to use hash tables :facepalm:
4:33:23
Bike
i think the question is whether undefined behavior means the entire program is undefined, or just the particular form
4:34:36
beach
Let me modify what I just said then: Provided AREF is required to signal an error, I think you example is a violation of Common Lisp semantics.
4:35:44
Bike
but i was less asking about whether this would be conforming and more with whether people would like or dislike it or what if that was how it worked
4:36:24
beach
You are opening a Pandora's box here. Now, Common Lisp implementations can do what implementations of C do, namely exploit all undefined behavior to improve performance at all cost. :)
4:37:09
beach
Ah, yes, I understand. Well, my opinion would be that the definition of AREF should be fixed, so...
4:37:35
Bike
i recently spent like two entire days chasing down bugs caused by c compilers doing that, so i have no desire to continue that into lisp
4:39:09
Bike
i mean, let me put this another way. should WSCL or whatever else require type errors to be signaled by the operator, or could it just say that an error is signaled somewhere? for example, at any point after the violation is inevitable.
4:40:23
Bike
in any case i'd like the situation clarified, since as far as i can tell CLHS is ambiguous here
4:40:59
beach
rotateq: As Bike pointed out, simple arrays can very well have fill pointers and such.
4:41:37
rotateq
Oh okay, I wasn't too sure, so it was good again we talked about it. I just tried then a simple example in SBCL.
4:42:00
Bike
any phrasing would probably be pretty involved, but i think that's sort of required. the standard is kind of... vague in this area
4:42:05
rotateq
beach: All C implementations? I read some time Symbolics had their own too as with Ada and Fortran. :)
4:42:26
Bike
there's a distinction made between "unspecified" versus "undefined" consequences, and the distinction is that unspecified consequences are "harmless"
4:44:39
beach
Bike: It looks to me like we (collectively) now have the ability to improve on the standard. Exciting!
4:46:56
rotateq
beach: As I often see, even more things to learn about what others so easily call just "trivial" and that's way more worth to explore and deepen as just flying in too abstract and "modern" things.
4:47:29
Bike
is it? i mean, i can imagine it being so, but on the other hand, maybe if the error is signaled earlier the program is stopped before it can get halfway through a state change
4:47:55
moon-child
the program is probably already halfway through a state change when it figures out the thing that it's going to index later is not an array
4:48:19
moon-child
realistically, you are not going to recover state from a program which fails a bounds-check
4:50:53
moon-child
I don't know. But I can imagine cases where it would matter, esp. in context of other transformations
4:52:10
Bike
the other reason i'm thinking about this is that in a few cases, it is outright impossible to signal an error at the violation point
4:53:13
Bike
if you have (let ((f (the (function * float) f))) ...), there's no way (in general, but practically speaking, pretty much at all) for the implementation to determine f will return a non-float until it actually does
4:54:11
Bike
strictly speaking with the way function types are defined, it could technically work out, but nonetheless the error would be some time after the THE is evaluated
4:59:28
Bike
oh, and i forgot there is an analogous situation where the clhs does sort of say something
5:00:07
Bike
in 3.5.1.1.1, it says that when an error is signaled for a call problem (too many arguments etc), "it might be signaled at compile time or at run time, and if signaled at run time, it might be prior to, during, or after executing the call"
5:07:27
Bike
anyway mostly i want to sound out if someone has some code that this behavior would ruin, that kind of thing. and i'm just fishing for thoughts.
5:26:16
pillton
The (function * float) example is a good one. I think AREF should signal an error though. I think there should be another operator or operators introduced which either avoid using AREF or ensure that every call to AREF does not require checking its arguments.
5:28:58
beach
I was just wondering what the advantage would be to signal the error early. I can imagine that there would be performance advantages. However, nothing prevents an implementation from detecting the error early, but not signaling it immediately. Instead, it would have two branches, one of which evaluates the intermediate forms and then signals an error.
5:34:43
beach
Does that make sense? I mean, we would then have the performance advantage in that the normal branch could be optimized to assume the right type, but we would still respect a more strict definition of semantics.
5:40:29
beach
More generally I think. I am contemplating a way to maintain the possible performance advantages with an early detection while still respecting the semantics of individual operators doing the signaling.
5:40:31
beach
So (let ((a (foo))) (map nil #'print a) (aref a 0)) would turn into (let ((a (foo))) (if (arrayp a) (progn (map nil #'print a) (aref a 0)) (progn (map nil #'print a) (error 'type-error...))))
5:42:00
beach
In the "normal" branch, the call to MAP could then be optimized to sasume A is an array.
5:42:43
beach
But the MAP operation would be performed in both branches, thereby respecting the semantics that AREF has to do the type check.
5:43:54
pillton
I mostly worry about checking that the index is valid, which you still need to do in your example.
6:07:00
moon-child
beach: (let (a) (if cond (setf a definitely-an-array) (setf a who-knows-what)) (aref a 0))
6:08:27
moon-child
in that case, hm, you could hoist the check without any observable change in behaviour
6:08:49
moon-child
(if cond (setf a definitely-an-array) (progn (setf a who-knows-what) (check-type)))
6:09:00
ns12
Hello. If I have a string stored in a file, and I want to use that string as a string literal in Common Lisp code, I could use the hash-dot `#.` reader macro to read the string from the file. If I produce an executable of this program, the user will not need to have the file present because the file is only read at "read-time". My question is: how
6:10:57
moon-child
(however given more complex control flow such a simplification would no longer be feasible)
6:12:19
rotateq
and when you have a string with "foo" in this .lisp it is read directly as an object of class simple-string
6:14:02
ns12
From my understanding, the three phases are: read-time, compile-time, and run-time. During run-time, I can have read-time and compile-time by using COMPILE and EVAL. During compile time, there will always be a read-time phase. But where is the run-time? During read-time, where is the compile-time and run-time?
6:17:21
rotateq
or when using the #+sbcl(+ 1 2) it first looks if :SBCL is in *features* at readtime but if not gives back (values) directly
6:20:12
White_Flame
ns12: the phases really only have to do with compilation itself. It all happens "at runtime" and whatever is in the image at that time is available to any code at any phase
6:20:42
White_Flame
EVAL, however, does not invoke the reader. It starts from already-read source code
6:21:25
ns12
I think I need to study the HyperSpec. What is the relevant chapter? Is the chapter 3 "Evaluation and Compilation"?
6:22:22
White_Flame
the reader converts "(+ foo #.(+ 2 3))" to the list form (cl:+ cl-user:foo 5), assuming *PACKAGE is cl-user
6:22:46
White_Flame
the compiler then takes that plain list of symbols etc and converts it to another executable form
6:24:33
White_Flame
so when the reader is doing its thing, the current package matters, and it has to evaluate #. forms before any compilation of what it's working with has a chance to be compiled (because the compiler needs the reader to finish)
6:26:40
White_Flame
because the reader has to read that entire PROGN form before executing it, and the etc: package doesn't exist yet, meaning etc:init will fail the _read_ step
6:27:07
ns12
I see. The reader is a parser that converts strings to Lisp lists. The reader is programmable using reader macros. The reader will evaluate #. forms.
6:27:17
White_Flame
the actual creation of the symbol in the source code list (etc:init) fails, because that package doesn't exist at that time
6:27:41
White_Flame
and the reader is written in lisp itself, and all is executing in the same image :)
6:29:00
White_Flame
once these notions start being more known, the spec becomes much easier to read. It's not a tutorial at all
6:30:08
White_Flame
ns12: and once the reader is done, pretty much none of the rest of the system ever knows or cares about the original string representation. It's all about forms (which is what macros deal with, too)
6:30:49
rotateq
yes ns12, as it calls functions when defined readmacros are seen. the readtable works as a lookup-table
6:32:36
White_Flame
so for instance in your .asd file, any file that has been loaded before the current one should have all its macros, functions, toplevel vars, etc available
6:33:41
White_Flame
the real muckety muck is loading from .fasl, though. If you have purely read/compile time effects that leave things set, those won't necessarily reappear next time you load your project
6:33:46
ns12
What if the user-defined function is in the same file as the #. form that calls the function? I think EVAL-WHEN is needed, no?
6:34:38
White_Flame
because of stuff like (in-package), some forms have to be read one at a time, because they affect how the next one is read
6:34:51
rotateq
so better doing such things in files that are processed before, as with pushing keywords to *features*
6:42:13
ns12
White_Flame: "... the etc: package doesn't exist yet, meaning etc:init will fail the _read_ step" - Is this because the reader will try to intern `init` in the symbol table of the `etc` package, but that symbol table doesn't exist yet?
6:51:50
White_Flame
(well, with a single colon, even manifesting a new symbol will still not be exported, and still error)
8:37:26
mgl
Anyway, the above is on SBCL, but the same happens on all other lisps I tried AllegroCL, CCL, CMUCL, ECL.
8:41:26
jackdaniel
If the restartable-form is a list whose car is any of the symbols signal, error, cerror, or warn (or is a macro form which macroexpands into such a list), then with-condition-restarts is used implicitly to associate the indicated restarts with the condition to be signaled.
8:42:10
jackdaniel
somewhat confusing requirement (especially given your use case) but that's what it says
8:47:37
phoe
tl;dr if you are in the debugger because of condition A, and then you do something that signals a nested condition B and you end up in a nested debugger, then you usually do not want to see restarts that are strictly related to condition A because they would be confusing
8:48:14
phoe
usually you want to only see restarts not associated with any condition *OR* those associated with condition B
8:48:31
phoe
and that's what WITH-CONDITION-RESTARTS does, and what RESTART-CASE implicitly does as well
8:49:46
phoe
if inside (progn (error "one") 1) you instead do (let ((error (make-condition ...))) (with-condition-restarts ... (error error) 1)) then you will be able to reproduce this behavior
8:54:30
mgl
phoe: Thanks. I'm aware of the condition-restart association, but this restart-case magic caught me offguard.
8:57:30
phoe
mgl: sure; if condition-restart association is one of the least known features of CL, then the fact that RESTART-CASE performs it implicitly is surely one of the least known features of the least known features
9:54:01
rotateq
haha i can violate the rule i follow every other time from physics studies :) "which unit?"
10:37:45
ns12
For making CRUD web apps, is it conventional to use the combination of sbcl, cl-hunchentoot, cl-who, and cl-postmodern?
10:38:29
jackdaniel
it is (for many people), except that some libraries doesn't have cl- as the name prefix
11:11:48
flip214
there's the parenscript library - it allows to push (a subset of) CL code as javascript to the client
11:12:45
flip214
which is nice for simple checks of input fields; eg. in lisp you'd have (defun check-field (stg) (cl-ppcre:scan "^regex$" stg)) and this snippet can be sent as JS to allow front-end verification without code duplication
13:08:25
gamaliel
Hi, common lisp newbie here. I was wondering whether there was a way to evaluate a quoted push statement. For instance, if I write (let ((a nil)) (eval `(push 2 a))), the eval statement fails, as it does not recognize 'a'. How could I express it so that (eval `(push 2 a)) makes a '(2)?
13:09:29
beach
gamaliel: You can't. Luckily, EVAL does not have access to the lexical environment. Only to the global environment.
13:10:25
lisp123
To add to that, this will work (for the reasons mentioned above) (let ((a nil)) (declare (special a))(eval `(push 2 a)))
13:10:58
gamaliel
Ok. I was trying to circumvent a recursion by crafting a nested loop in quotes, but got stuck when I had to do the inside statement which involved push.
13:11:34
phoe
if anything, you can defer computation using closures, like (let ((a '())) (lambda () (push 2 a) a)) - this returns an anonymous function that repeatedly pushes a 2 into some list, and then returns that list
13:15:00
gamaliel
Ok. I was just seeing if it was possible to generate unevaluated nested loops to create a list of combinations.
13:17:28
gamaliel
So, for example, to generate the combinations of 3 elements from {0,1,2,3,4}, I could write (loop for i from 2 below 5 do (loop for j from 1 below i do (loop for k from 0 below j do (list i j k))))
13:20:09
phoe
or rather, nothing that you'd commonly encounter in Common Lisp that's written commonly
13:20:52
gamaliel
Yeah, I'm very new to the language. Just tinkering around some exercise problems I found to practice.
13:21:12
phoe
iterating over three variables would be like (dolist (i (iota 5)) (dolist (j (iota 5)) (dolist (k (iota 5)) ...)))
13:24:35
gamaliel
That would be correct. I mean, it's quite readily solvable using recursion, but I wanted to challenge myself into the new parts of the language using a way to construct the loops unevaluated and collecting the final call afterwards.
13:24:58
phoe
at least, unless the requirement is to stay functional, I'd macro it away - write something that can expand into nested DOLIST or DOTIMES
13:28:35
phoe
but if you want it to be customizable at runtime then you have a slightly more involved problem to solve
13:30:49
gamaliel
The nested dolist seems like a very doable solution. I can modify it to use loop and it may just expand exactly the way I need it. Thank you!
13:32:50
phoe
gamaliel: the general way of thinking in CL is - if you need to operate on something in its raw, unevaluated form, then you most likely need macros
13:33:55
gamaliel
Ok. My experience doing that is limited to simple bquote() statements in R programming language for statistics courses. This will be a good refresher.
13:37:32
gamaliel
Something like this could be a start for the solution: https://plaster.tymoon.eu/view/2866#2866
13:40:13
gamaliel
Ok. I just planned to use it as an internal template. Recursive macros are really mind-boggling for someone who has only programmed in other languages.
13:41:10
phoe
recursion is recursion - remember to check your termination conditions, and try to expand your macros one step at a time
13:41:38
phoe
macro writing is basically writing pure functions that transform one soup of conses and symbols into another soup of conses and symbols
13:54:30
Xach
empwilli: rainer joswig has a funny post about running lisp on his macbook air m1 6000x faster than his symbolic hardware, and consuming 6 watts vs 1KW for symboilcs
14:04:41
Bike
::notify pillton "I think there should be another operator or operators introduced which either avoid using AREF or ensure that every call to AREF does not require checking its arguments." what do you mean?