freenode/lisp - IRC Chatlog
Search
7:07:37
flip214
can I tell QL to compile some systems (with all their dependencies), but not load them?
7:07:58
flip214
guess I can't, because compiling them might require some dependencies to be available...
7:09:07
jasom
flip214: on sbcl, if you don't have any threads running, you can fork a process, load them all and then exit.
7:14:45
jasom
even if it doesn't do what you want, it would be a good place to start to do what you're doing
7:24:26
flip214
hmmm, cliki.net says that my account doesn't exist.... was there a cleanup some time ago?
8:02:29
Shinmera
The primary problem with c-l.net is that like 99% of the pages hosted on it point to links that are long gone.
8:03:36
antoszka
There should be an automatic cleanup procedure. A bot that checks the links and removes them or marks them *dead*.
8:04:48
Shinmera
Either way the problem is that maintainers don't really care to begin with, or there is no real maintainer, so the links stay dead.
8:04:56
flip214
that might be the best solution, because the automatic fixing can be made more intelligent over time
8:07:13
flip214
then I could try to rig something up. (and I had to resist HARD to write "... to make cliki great again" ;)
8:08:39
Shinmera
Sure, but ease of collaboration and integration can go a long way to gaining traction. Cliki feels sluggish and dead.
8:08:48
flip214
any alexandria maintainers here by chance? I asked for permissions to gitlab about a month ago, so I can fix up some of the doc strings... and no answer yet.
8:09:29
flip214
the github interface to edit _any_ file (which just clones the repo and does a PR in the background!) is very slick.
8:11:26
Shinmera
There is definitely value in the idea of a central hub of information and advice for CL.
8:15:40
Shinmera
Anyway, the software and the "cliki idea" are only marginally tied together, so I agree with jackdaniel to a point.
8:19:38
flip214
but if people would hear that the CL wiki would run on PHP, wouldn't that be awful?? ;)
11:49:12
Shinmera
flip214: That's the point. In graph theory, a tree has no "root". Any node in the tree can potentially be considered its root, but that consideration is purely arbitrary.
13:19:46
Younder
edgar-rft, your implementaion conses like crazy. It will be slow for large datasets
13:22:10
Bike
though maybe i don't understand it anyway. is args supposed to be a list of numbers or a list of lists?
13:27:17
Younder
In compiler theory we use bitsets. Because they are a more compact representation of bool. Particularly in data flow analysis.
13:29:02
oleo
When the function receives its arguments via &rest, it is permissible (but not required) for the implementation to bind the rest parameter to an object that shares structure with the last argument to apply. Because a function can neither detect whether it was called via apply nor whether (if so) the last argument to apply was a constant, conforming programs must neither rely on the list structure of a rest list to be freshly consed, nor modify that list
13:48:59
aeth
anyway, this is the final form: (defun everycons (&rest args) (reduce #'cons args :initial-value nil :from-end t))
14:16:54
R0B_ROD
Everytime I try to build something or pull a library it says quicklips isnt installed
14:26:00
pjb
edgar-rft: so there are ones who are more ones than others… (everyone '(1 1.0 2/2 1.0d0)) #| --> nil |#
14:40:26
edgar-rft
pjb: float 1.0 could be an arbitrary accumualtion of floating-point rounding errors, so you can't surely say if float 1.0 is *really* fixnum 1.
14:41:48
edgar-rft
phoe, implement this: <http://68.media.tumblr.com/tumblr_kud6opJD1s1qa31pso1_r2_1280.gif>
14:42:11
Younder
Type systems are notoriously hard to get correct, and embarrassingly easy to get wrong.
15:06:21
zulu_inuoe
I know people complain about LOOP, and show off their loop-fu, but I don't think I've ever even came close to writing a DO that works, even a simple list iteration
15:07:57
marvin2
i think most people who dislike loop don't use do but dolist, filter, map, reduce, etc
15:14:27
marvin2
step keeps getting evaluated (and assigned to var) while test-form evalutes to true. when test-form evaluates ot false, result-form is returned
15:16:40
zulu_inuoe
I think the main thing that threw me off (and something wrong in your example) is that test-form needs to be wrapped in parens, and is followed by multiple return values
15:26:25
phoe
often the only thing that you need in loops are updating the variables and checking the test
16:00:20
aeth
I use do. It's not that hard once you learn it. It's just a let that updates itself each step. do* is just a let* that updates itself each step.
16:02:50
aeth
You can even use it basically like a let except with a (t ...) around the let body: (do ((foo 42) (bar 37)) (t (values foo bar)))
16:05:07
aeth
A properly-written do often needs no body, if it is functional. The form above is one useless example. The body is mostly useful for side effects, when setf makes the iteration more readable.
16:06:38
aeth
Just modifying my example to have it run 3 times: (do ((foo 42 (- foo 2)) (bar 37 (1+ bar))) ((= bar 40) (values foo bar)))
16:07:32
aeth
This will increment bar to 40 (i.e. 3 times) so foo will be 42-6 (i.e. 36). The final return value will be (values 36 40)
16:07:37
pjb
zulu_inuoe: consider (loop :while <test> :do <body>) vs. (while <test> <body>) : it may be good to define a set of simple iteration macros. while, until, repeat, for, etc…
16:09:56
pjb
(also, writing those simple looping macros, expanding to do/do* is a good exercise to understand how things work…)
16:10:59
aeth
Right, in CL almost all iteration (in most implementations) will ultimately become let + tagbody + go, like do expands to in most implementations. Expanding it to do is just a time saver with a more human readable intermediate step.
16:11:28
aeth
I'd much rather deal with do than deal with tagbody, although I'll probably read both forms (expanding to do, and expanding that do to tagbody) just to be sure.
16:12:39
pjb
eg. writing a loop with tagbody by starting (tagbody (go :test) :loop …) to optimize out one jump per iteration and stuff like that :-)
16:13:27
aeth
And all do forms have an implicit tagbody (although your editor probably only supports gotos and labels properly in tagbody, not do-foo and prog) so they really are intended to be useful as an intermediate form.
16:14:00
aeth
You never have to drop down to tagbody if you're using do, because you can put some confusing goto in do itself (but don't do this unless you're writing a macro that targets do, please)
16:15:01
aeth
tagbody is evidence that CL is still, in 2017, the only serious Lisp for general purpose high performance uses.
16:15:23
pjb
(do (…) (…) :retry … (cond ((donep) (go :continue)) ((failedp) (go :retry))) … :continue)
16:42:35
jasom
zulu_inuoe: https://github.com/sbcl/sbcl/commit/585853373a4b43aba7ac740f1fe50bd622f673eb
16:54:31
jasom
Fare: hmm did you get my memo asking about CL conditions and using them for asynchronous aborts?
16:56:38
jasom
I'm not sure how what you're doing in Gerbil is fundamentally different from restarts that escape the critical section and resignal, allowing higher levels of abstractions to do the same.
16:59:14
jasom
I read a rant about asynchronous aborts, not sure if I read sufficiently far into the thread
17:00:37
Fare
I'm sure you could do it with restarts, as long as lower-level failures were handled, e.g. failures in the middle of memory allocation, in the middle of having allocating a resource and binding it to a variable that the user will watch with his own finalizers/restart/whatever
17:01:57
Fare
what if the async abort happens after the open, before the binding to x, and before any unwind-protect inside that let?
17:03:50
Fare
java has try-with-resources pattern... but it would have to be extended to handle atomic return from the function that provides the resource
17:04:23
jasom
so each binding construct would have to ensure that code in the bindings either completely finishes or cleans-up after itself
17:04:40
phoe
how can that restart know when the async abort happened? can it analyze the stack or something?
17:04:58
Fare
I wonder how Rust does it. But then again, I don't think even Rust has async abort. I hear the Swift guys are interested in it... good luck to them, I'm curious what they come up with.
17:05:08
phoe
like, before/during/after the open, before/after binding to x, before/during the unwind-protect...
17:06:12
jasom
Fare: which is why the condition needs to be resignaled once it's safe at the current level of abstraction
17:06:13
phoe
jasom: yes, it would be required for them to provide much more fine-grained information to the condition system
17:06:27
Fare
to kill a thread without killing the larger program, you need to sync it to an atomic transaction *at the highest level* that any other thread cares for.
17:07:16
jasom
phoe: not really; the dumbest way would be to not allow any interruptions until the binding happens, once the form has begun to be evaluated; blocking calls should defer this until no more syscalls are required.
17:07:22
Fare
jasom: exactly. So you must first get everything right at the low-level, then have a mechanism to let the user percolate the information up the abstraction tower
17:07:42
jasom
Fare: conditions are sufficient for the latter, but work would need to be done for the former in CL.
17:08:20
phoe
jasom: so you enable interrupts inside (open), disable them once you get the value, bind the value to x, enable interrupts again?
17:08:35
jasom
And it sounds like Gambit already does a lot at the low level, compared to the typical CL implementation
17:09:38
Fare
phoe: remember, there are now several levels of interruption, one for each level of abstraction.
17:10:01
Fare
phoe: and I don't know how to keep higher-order functions modular in presence of that.
17:10:11
jasom
phoe: at the low level, you want two things to happen with (let ((x (open))) ...) 1: either x is bound to the open file, or the file is not open. 2: We don't disable interrupts for the entire open call, which could take an unbounded amount of time.
17:12:29
jasom
so one way would be the following: 1) disable interrupts 2) have open establish an unwind-protect 3) inside that unwind-protect enable interrupts 4) disable interrupts just before leaving the unwind protect 5) establish the binding 6) reenable interrupts
17:12:56
jasom
however, this still leaves the issue of (let ((x (open))) (unwind-protect FOO (close x))) as having an unsafe region
17:13:48
jasom
wheras (let (x) (uwind-protect (progn (setf x (open)) ...) (when x (close x))) would be safe
17:14:25
jasom
but it's only safe if similar work is done to setf to make sure that it either completes, or the side-effects of the value form are not visible.
17:15:31
jasom
what if "open" is opening a device, and you must send shutdown codes to the device to ensure that it is back in a sane state; then just closing the file when interruptied is not okay...
17:16:29
jasom
phoe: right, but now open must be slightly different; you need to finish the open any time that the underlying syscall succeeds.
17:17:11
jasom
phoe: correct, and whenever open is in a binding or assignment, if open succeeds, so must the binding/assignment
17:17:14
phoe
OR if the syscall succeeds but open fails after that but before it returns, a handler must close the device.
17:17:49
jasom
phoe: nope, that's possibly insufficient; OPEN doesn't know about the higher levels, so it should succeed and then pass the buck for handling the signal
17:18:09
jasom
then assignment doesn't know about higher levels, so it should succeed and then pass the buck similarly
17:19:02
phoe
and how can the outer code know if the success is a successful success or a failed success?
17:19:23
jasom
phoe: let's call the open syscal "sysopen" for clarity; if "sysopen" completes, this imposes side-effects of unknown importance on the higher levels
17:21:17
jasom
if you impose both of those, then (let (x) (uwind-protect (progn (setf x (open)) ...) (when x (close x))) will be safe regardless of when you get interrupted.
17:22:00
jasom
There are a lot of moving parts, so when Fare tossed out the phrase "safe asynchronous aborts" casually in a list of what he liked about gerbil, it got my attention.
17:22:59
phoe
yes, there are a lot of them. I needed a moment to make what seems like a working model of that.
17:24:01
phoe
But you can reduce that case to assignments only by transforming (let ((foo bar)) ...) into (let (foo) (setf foo bar) ...)
17:24:28
jasom
phoe: each level of abstraction needs to ensure that any visible side-effects either 100% happen or 100% don't because otherwise it's not a solid foundation to build upon
17:25:26
jasom
(let ((foo bar)) (unwind-protect ...)) needs to be transformed into (let (foo) (unwind-protect (setf foo bar) ...))
17:27:00
jasom
if you allow asynchronous aborts, then (ESTABLISH-BINDING (UNWIND-PROTECT )) has an unsafe point between the binding established and the unwind-protect being established
17:27:41
jasom
(let ((X Y)) (unwind-protect Z (cleanup X))) <-- race condition where X might not be cleaned-up
17:28:51
phoe
I'm confusing the UNWIND-PROTECT from the first LET with the UNWIND-PROTECT from the second LET because they do different things.
17:29:16
jasom
and this can't be solved in the general case in a sane manner because consider (let ((x y) (z (loop-forever))) (unwind-protect ... (cleanup x))
17:29:59
jasom
phoe: right, and then you interrupt it, and you never reached the unwind-protect so X is left not cleaned up
17:30:33
phoe
welp. so what do you do, a tower of unwind-protects that establishes a cleanup for each bound variable?
17:31:25
phoe
(let (x z) (unwind-protect (progn (setf x y) ...) (cleanup x)), where ... is (unwind-protect (progn (setf z (loop-forever))) (hypothetic-cleanup z))?
17:32:25
phoe
when we consider the bindings to be parallel, we get undefined state "between" the bindings
17:32:41
jasom
phoe: that's a *very* good question that I don't have an answer for, except rewriting code so that you don't ever rely on a let over an unwind-protect... My preferred style in not lisp things is to have the equivalent of a "with-foo" type form that allows it to escape the scope, and then just not ever use let for binding values with side-effects.
17:34:02
jasom
C++ lets you do this because the destructor is *always* called when the value leaves scope, so an unwind-protect is not needed
17:34:56
jasom
phoe: the order the bindings are established in (not the order that the value forms are evaluated in)
17:36:05
jasom
(let ((foo 1) (bar 2)) => (let* ((#:foo-gensym 1) (#:bar-gensym 2) (foo #:foo-gensym) (bar #:bar-gensym))
17:36:38
phoe
So once we solve the case for LET*, which is solvable with a tower of unwind-protects, we solve the case for LET.
17:37:14
jasom
right, but LET doesn't know what to do for cleanup, so we need to combine LET and UNWIND-PROTECT into a single form
17:38:12
phoe
https://github.com/phoe/gateway/blob/577c822b869118c762cf3865ad4aa0a5f0d6a0f1/utils/macros.lisp#L48
17:39:28
jasom
so (protected-let ((X Y Z)) W) => (let ((X #:PL1)) (unwind-protect (setf X Y) (progn W) (unless (eq X #:PL1) Z))
17:40:05
jasom
phoe: but it then still requires all side-effecting standard library functions to be rewritten
17:41:23
phoe
and basically implement a PROTECTED-COMMON-LISP package that somewhat-portably reimplements the language in that manner.
17:41:42
phoe
but then again, we're dealing with stuff on the boundary of the CL image and the operating system, so I don't think much of it can be done in actual portable CL.
17:42:08
jasom
And I understand now; Fare's point is that Gambit's preexisting ability to move thunks at nearly any point in execution means it's closer to being correct than most other lisp implementations
17:42:29
phoe
but the upside is, if your package does (:use :protected-cl) instead of (:use :cl), you might be much safer.
17:42:54
phoe
and if you dare to recompile all packages that (:use :cl) to (:use :protected-cl) instead, woohoo, oh boy.
17:43:45
jasom
But the existence of let over unwind-protect as a code idiom means just fixing the implementation of CL is necessary, but not sufficient
17:49:53
aeth
I think at this point it would just be easier to treat your variant of CL as a separate programming language and forget about translating the libraries, just treating them as foreign libraries.
17:50:26
jasom
phoe: right, the transformation from LET into PROTECTED-LET can't be done automatically
17:51:19
jasom
aeth: the big downside to treating them as foreign libraries is you lose a lot of asynchronity; you'd need to disable interruption for the entirety of the forign code being run.
17:52:20
aeth
If you're going to make your own CL variant, you could even make things like alexandria and cl-ppcre core to the language.
17:52:27
phoe
aeth: Lisp isn't popular as a language, therefore its libraries aren't popular either. I just reduced the problem to 0.
17:53:00
aeth
phoe: Except one can assume that the same libraries would be popular no matter the size of Lisp, unless the size of Lisp causes new libraries that are even better.
17:53:26
aeth
malice: JS to CL wouldn't work because fast JS relies on JIT magic to be decently performant.
17:54:10
aeth
Without JIT magic, you're left with a slow language with a handful of very basic types that makes everything slow (like using double-floats for all numbers... JIT magic if it can prove it can turn those into integers)
17:56:11
p_l
IIRC, the naive JS->CL compilers used to be some of the fastest options out there before V8, and you could probably abuse the recompilation more (especially if willing to use implementation-specific bits)
18:03:40
malice
jasom: and why :) I know that SLIME provides nice level of interactivity and provides (quite good) interface for many CL's features that are otherwise a bit harder to use, but I've seen someone talk in here about smalltalk being more interactive
18:03:42
jasom
malice: though really it's a bag of features; I can compile a function, profile it, get the disassembly, with instruction-level profiling, modify the function, recompile it, run it again. All in about a minute, without ever waiting for anything (the only time I'm waiting is when I'm collecting profiling data, which is usually seconds tops)
18:04:02
Shinmera
malice: There's no killer feature, it's just the local optimum of things I prefer.
18:04:18
jasom
malice: smalltalk and lisp have historically had a lot of cross-pollination as far as tooling goes.
18:05:13
jasom
the other thing is that CL mostly gets out of my way, and when it does get in my way, I can change it to not be in my way anymore.
18:05:15
malice
jasom: do you profile with SBCL's statistical compiler? I also haven't heard of instruction-level profiling, care to explain a bit more?
18:05:34
jasom
malice: if you use sb-sprof, then disassemble you get instruction-level profiling output
18:08:13
aeth
malice: consistent syntax, real macros (follows from the syntax), good representation of numbers, good performance. Also, yes, fast recompiles, the SLIME REPL, and disassemble are good.
18:08:48
lambda-smith
Not kiddings about SLIME. It's so good, it makes programming in other languages (even other Lisp dialects) feels like a chore.
18:09:04
aeth
The only languages that come close are Schemes, but they usually fail on performance and the practical development side of things (i.e. SLIME).
18:10:20
lambda-smith
aeth: Scheme really needs a better IDE. Geiser is good, but still no where near SLIME.
18:12:18
aeth
When I have time to put in the month or so to get cl-scheme working, I wonder how I should get its REPL working. Geiser or maybe even try SLIME? Apparently one Scheme can already work with SLIME.
18:13:29
phoe
or rather, you'd get the stack traces and everything, but you'd lose the interactivity
18:14:59
aeth
lambda-smith: A compiler (transpiler?) and (if I get to it) interpreter of Scheme written in CL, sort of like Pseudoscheme but not pseudo and r7rs-small instead of r4rs.
18:15:53
aeth
Hygienic macros and continuations are the tricky parts, as well as the lack of portable lexical scope globals in CL.
18:16:09
aeth
For continuations, I'm probably going to just use CL lambdas with continuation passing style.
18:16:32
aeth
Ideally, anything in Quicklisp should be runnable within cl-scheme with some wrapping.
18:20:04
aeth
lambda-smith: I already have many functions written in CPS and tested with #'identity, e.g. (cl-scheme::%s-+ #'identity 2 3) => 5
18:20:45
lambda-smith
So you are going to implement cl-scheme entirely in a continuation passing style?
18:21:46
aeth
Doing some analysis on it could remove some of the performance disadvantages with things like e.g. (+ 1 2 (/ 3 4))
18:22:18
aeth
But I think even written mostly naively cl-scheme on SBCL should beat most existing Schemes because SBCL is really fast.
18:23:38
aeth
So I basically just need to complete it (hygienic macros and the global environment will be tricky), get the FFI working both ways, get the REPL working, and benchmark it and see.
18:24:03
phoe
yes, but I can imagine you finishing it and then going to troll all the #scheme people
18:25:02
aeth
In fact, if Quicklisp interoperability works, it bootstraps a very large library ecosystem, too.
18:25:23
phoe
aeth: I know it's not trolling, just, in the context of the great scheme-or-lisp debate...
18:26:35
aeth
My ultimate goal is to use CL (with reader macros!) as a multi-language platform, not unlike Racket or (heh) JavaScript.
18:27:27
lambda-smith
I know Racket also has the same goal, but I really hate Scheme/Racket hygenic macros...
18:28:00
aeth
Certain CL implementations could in the long run be modified to make certain things faster, perhaps even e.g. continuations.
18:28:48
lambda-smith
aeth: I dream of that day. CL with a first-class support for continuation would be golden
18:29:21
aeth
Well, it would afaik be more like second-class, but transpiling languages (including perhaps a CL subset language) could treat it as first class.
18:30:58
aeth
Going much further than Scheme would be tricky, too. Some (e.g. C) require low-level things. Others (e.g. JS) require a JIT to be performant. Although maybe CL is powerful enough to have a JIT transpiler?
18:31:32
_death
every time I see this nonword "transpiling" I feel an urge to go to a different buffer..
18:32:15
aeth
_death: How else am I supposed to distinguish between writing a native code compiler in (SB)CL and writing a compiler *to* (SB)CL that then uses (SB)CL's native compiler?
18:33:13
lambda-smith
malice: It's a SLIME shotcut that let you dissamble a function that you current has the cursor on; try it.
18:33:29
phoe
lambda-smith: you know you've become a lisper when you no longer recognize Alt as a keyboard key
18:35:57
_death
it's just a personal peeve.. I see no reason to import javascript jargon for stuff that computer science has managed to discuss just fine for many decades
18:37:39
aeth
Third time saying this today, but JS-to-CL would never match the performance of a proper JS implementation because of how JS the language works.
18:38:05
aeth
If you want something like JS, Lua, Python, etc., don't 1:1 translate the language, come up with something superficially similar that would perform better when compiled to CL.
18:38:39
phoe
> transpiling languages (including perhaps a CL subset language) could treat it as first class.
18:39:54
aeth
phoe: Languages that compile to CL (including perhaps a CL subset language) could treat continuations as first class, but afaik CL itself cannot have first class continuations because of its restart system (iirc).
18:40:44
aeth
Although I might be remembering the wrong reason why CL doesn't have continuations but has better debugging than Scheme.
18:41:43
lambda-smith
aeth: You got it right. unwind-protect is the reason why CL doesn't have first-class continuation
18:42:42
lambda-smith
That, and one of the CL designer really don't like continuation (because it conflicted with the restart system)
18:44:14
lambda-smith
Bike: IIRC, CL designer claimed that unwind-protect is better than dynamic-wind because CL doesn't have first-class continuation
18:46:27
Bike
well yes, dynamic wind doesn't make a lot of sense if there's no way to abnormally enter a dynamic extent.
18:46:37
aeth
Sometimes what a language cannot do is pretty important. Only weakly related, I can imagine a 100% purely functional embedded language within CL (no need for any way to deal with side effects if it's embeddable into CL) that could make so many assumptions when compiled into CL because of being functional.
18:46:56
Bike
i'm just saying i don't think unwind-protect is why there are no first class continuations. it's rather because the CL designers didn't want them.
18:49:22
lambda-smith
Bike: That's what I suspected as well, but hey, that's what the CL designer claimed.
18:50:25
phoe
unwind-protect only covers the "exit dynamic extent" part; dynamic-wind also covers the "enter dynamic extent" part