IRC channel logs

2024-12-03.log

back to list of logs

<UncleRRR>Hi, everyone! I wonder if it is possible to write a procedure that does nothing here, neither would it return anything.
<UncleRRR>I tried (lambda ()) but it is not valid syntax seemingly
<UncleRRR>Or must a procedure cause some side effects or return something. No vain option and no equivalence to "pass" in python?
<COMPL_EXE>What's your use case for "pass" equivalence in Guile?
<UncleRRR>I am trying to construct a list and put a lot of pairs consisting of procedures in, so that I can iterate the list to call all the procedures dynamically. Which you can think of like a toy plugin system. I am using something like for-each and (lambda (x) (x)) to evaluate the procedures, but it would cause error if some pair has the car or cdr empty(with '() or #f), so I am considering to make a "vain" procedure as a placeholder instead.
<UncleRRR>I know it is fine to just like the placeholder return #f or '(). Just curious if a real procedure doing nothing and returning nothing is availuable in guile.
<UncleRRR>Well, that might not be a procedure actually. Just some stuff which won't give me an error, nor causing side effects, when put alone in the ()
<COMPL_EXE> https://docs.scheme.org/surveys/void-value/ Seems that Guile does not provide something like (void)
<UncleRRR>Oh, fine.
<UncleRRR>I can see that many other implementations have such thing. Why not guile? :(
<rlb>UncleRRR: perhaps (lambda () (if #f #f))
<rlb>That effectively just returns *unspecified*, but should be less guile specific.
<UncleRRR>Cool
<UncleRRR>The best approach up to now. Maybe for a real void procedure we have to change the core to support it.
<rlb>fwiw, if you "git grep -F '(if #f' module" in the source, you'll see that construct is used with some frequency.
<UncleRRR>mmm, I only see the need of supporting a void in core. And at least wrapping (if #f #f) up as a void procedure, with some explanation in document illustrating that the performance is at least as good as "pass" in python.
<UncleRRR>Is this dthompson-web the guy written chickadee and haunt? Or another random dthompson?
<mdj>sneek: botsnack
<sneek>:)
<mdj>sneek, later tell UncleRRR There *is* core support for "void" in Guile. You can do (lambda () *unspecified*). However. the if construct is more portable, as rlb said (and the compiler makes sure nothing is "done" at run time).
<sneek>Okay.
<apteryx>does the &error srfi-35 condition type in guile has any fields? such as a message field?
<mwette>apteryx: I think you need to add a message with `make-compound-condition'
<mwette>I think Guile has `make-exception' which seems to be the same. See Sec 6.11.8
<apteryx>mwette: thank you
<dthompson>I tried to chime in on the "returning no values" conversation yesterday from my phone but the libera web client wasn't working...
<dthompson>for whatever it's worth now: if you want to return nothing, then just do (values)
<dthompson>that returns 0 values
<dthompson>(which is different than returning unspecified)
<dthompson>for example: https://gitlab.com/spritely/guile-hoot/-/blob/main/lib/fibers/channels.scm?ref_type=heads#L63
<dsmith>ISTR there was some debate a while back about if (values) should be returned by (if #f #f)
<civodul>zero-value-returning procedures are actually surprisingly annoying
<civodul>it’s easy to unwillingly find yourself calling such a procedure in a context that expects one value
<civodul>and it’s not checked statically, so easy to get wrong
<dthompson>having done it both ways I like the semantics of returning 0 values when you have nothing to return
<dthompson>compile/runtime stuff could be improved though
<civodul>yes, i like the spirit of it
<civodul>i’m just surprised how easily it trips me up :-)
<mwette>Is (values) well defined outside of call-with-values context?
<old>mwette: it is undefined says the doc
<old>*unspecified
<mwette>old: Unspecified as in a conforming Scheme implementation could raise an error ?
<old>Unspecified as in don't assume wathever work today will work tomorow I suppose
<mwette>Yep; It seems `(if #f #f)' may be more portable than `(values)' (but doen't matter for Guile).
<dthompson>hey guilers, we at spritely have launched our first ever supporter drive! if you'd like to support our work on guile projects like hoot and goblins, please consider a donation! https://spritely.institute/donate/
<robin>hrm...do i have to take any special precautions when running e.g. the gabriel benchmarks? the results i'm getting are pretty extreme, although not actually unbelievable
<robin>an example: https://paste.debian.net/1338074/ -- scheme version of TAK, https://paste.debian.net/1338075/ -- elisp version of TAK
<robin>(0.06 s with guile scheme, 4 s with gccjit-compiled elisp, 9 s with guile elisp)
<robin>(and that's with an extra set! each iteration in an attempt to foil a hypothetical optimization i'm quite sure guile doesn't actually do, similarly i'm pk'ing the results during each round for benchmarks with fewer repetitions)
<robin>similar results for other basic gabriel benchmarks like STAK and TAKL
<robin>i think i'm measuring this correctly, but it seems prudent to ask before claiming in public that guile can be 100x faster than aot-compiled elisp :)
<rlb>umm, yeah, perhaps :)
<robin>(however, from a quick look it *appears* that emacs "native compilation" is just applying template jit techniques to programs compiled to emacs's ancient bytecode format, which for instance always looks up functions via symbol constants; i'm sure it made sense in the '80s but gccjit is not magic)
<morenonatural>this could still be the year of guilemacs
<morenonatural>the year's still not over
<old>so I can finally configure emacs with Scheme instead of horrible elisp?
<robin>morenonatural, https://emacsconf.org/2024/talks/guile/ will be broadcast this saturday, so...
<robin>old, theoretically, yes, simply accessing scheme variables with the same @ operator as scheme. in practice i'm going to suggest that guile incorporate a CL compiler (sufficiently reflective to disguise itself as an elisp compiler) for various reasons
<robin>however maybe we can compromise and get everyone to code in ISLISP, which has the interesting property of being implementable as a compatibility layer on top of both cl and scheme, iirc :p
<robin>(er, or rather you'd access elisp variables via guile modules corresponding to lisp symbol namespaces, going from scheme->elisp)
<mwette>CL is dynamically scoped, right? But now elisp core is lexically scoped. I think elisp direct is going to be better.
<mwette>^ "elisp core" means all code in the emacs dist, I believe.
<robin>mwette, cl was the first "traditional" lisp dialect to use mostly-lexical-scope. emacs got it as an opt-in feature many years ago and it's still not the default
<robin>(although "-*- lexical-binding: t -*-" is pretty much universally used in elisp programs nowadays)
<morenonatural>it is, though you can still find *MUCH* important code without lexical
<morenonatural>geiser & tramp as some examples
<robin>oof. i hadn't actually looks for counterexamples in the emacs source :/
<mwette>release notes for emacs-28 stated everything was lexical IIRc
<morenonatural>I can double check
<robin>i'm basing that on the elisp manual (info "(elisp) Lexical Scoping") "The dynamic binding was (and still is) the default in Emacs [...] Emacs is moving towards using lexical binding [...] with the goal of eventually making that the default."
<robin>...which could easily be out of date
<robin>but i did sort of think that emacs *had* flipped the switch at some point, and lexical-binding is t in my scratch buffer
<robin>mwette, you're right, it is the default now https://lists.gnu.org/archive/html/emacs-devel/2020-08/msg00237.html
<dthompson>robin: that is verrrry interesting re: relative benchmark performance
<dthompson>have you seen the HN thread about your upcoming emacsconf talk?
<dthompson>I see the following criticism emerging already: emacs does native compilation now so guile-emacs doesn't accomplish anything
<dthompson>but if your benchmark results are indeed accurate, it is pretty clear counter to that argument
<morenonatural>ok, lexical-binding is set in all geiser & geiser-guile files, but I still see plenty of global/static vars ( `defvar' ) used as dynamic scoping
<morenonatural>probably the same with tramp
<rlb>(...does the elisp compiler just rewrite those as fluids or something right now?)
<morenonatural>I don't think it's feasible to replace `defvar's with `let' bindings / `defun' calls in this lifetime around tramp code
<robin>dthompson, yeah, i've been biting my tongue :)
<rlb>ACTION wonders -- that's what lokke does for clj "dynamic vars"
<robin>rlb, elisp actually uses dynamic-wind based "dynamic binding" atm :( guile fluids are way too slow, and i have a *beautiful* 30-line implementation of buffer local variables but it doesn't work in guile-emacs because c and delimited continuations don't go well together
<robin>(optimizing guile fluids + adding a hack for context-sensitivity is the obvious short-term solution)
<rlb>Yeah, i wondered about the performance, but given everything else I needed, I just set that aside for now :)
<rlb>(And in clj, they really are mostly for per-thread bindings, etc.)
<rlb>(bindings that can be varied per-thread I mean)
<robin>(guile fluids are very obviously designed with the assumption that a program will use just a handful, looking them up might involve a linear search or something? an easy problem to fix, just not a priority for scheme)
<robin>ah that makes sense, that's how special vars work in pretty much every multithreaded CL afaik
<rlb>(They do have at least one subtlety, added early in clj's history -- binding conveyance. In some cases the current binding values "convey" to threads created from that thread. https://clojure.org/reference/vars#conveyance)
<dthompson>robin: your buffer local variable implementation is in c?
<robin>dthompson, at the moment, it's not only in c but controlled by emacs :( :( :( i tried integrating that 30-line gem and quickly discovered that one cannot, in fact, save partial segments of the c stack, and there's so much back-and-forth between c and lisp in emacs...
<robin>what will probably make sense in the short term is a tiny fork of guile with fast fluids and hacked-together support for context-local fluid bindings
<robin>(unless there is some obviously good solution to the problem that's worth upstreaming)
<dthompson>where does the stack capturing come in?
<dthompson>I'm not familiar with how emacs implements buffer locals
<robin>i've forgotten exactly how emacs does it, i'm sure it will resemble a hacky equivalent based on guile fluids. the problem with the pure-scheme implementation is that it used delimited continuations to emulate variants of dynamic binding, and delimited continuations simply don't work when c frames are involved between the prompt and the abort (call/cc can work in that context via setjmp and longjmp, i think)
<robin>(there are tricks to potentially get around that, but probably too difficult compared to just jamming the feature into the fluids implementation)
<dthompson>yeah, continuations with some c stack in them are non-resumable
<dthompson>assuming a current-buffer fluid/parameter, would it be possible for buffer local references to expand to a lookup of a value in the current buffer?
<dthompson>(I'm guessing this naive but I'm curious :)
<robin>dthompson, that would be doable, yes, at the cost of every special variable access going through the buffer info, since any variable can become buffer-local at any time
<dthompson>hmm yeah need a fast path
<dthompson>otherwise you're paying a hefty penalty for every var lookup... not good
<dthompson>you're absolutely right that the overhead for fluid-ref is a lot
<robin>dthompson, now that i'm thinking about it, the solution might be as simple as allowing dynamic states to inherit from/"shadow" fluids from other dynamic states, plus a couple other operations on dynamic states
<robin>(or view it as a stack of dynamic states, or something along those lines)
<robin>not exactly that, but something along those lines. which would slow down fluid-ref slightly but wouldn't absolutely wreck performance
<robin>that's not exactly the right way of explaining it but maybe gets the gist across
<robin>now imagine implementing this kind of thing in standard common lisp, and that's the simplest answer to "why not just write an elisp compatibility layer for sbcl" and the like ;)
<rlb>Not a big consideration, but if y'all get closer to proposing something, I'd be interested, in case there's a way that it can be done that's also friendly to clj, and better than fluids there...
<rlb>(But yeah, likely not a concern to weight very heavily unless it also helps elsewhere.)
<robin>rlb, what are the difficulties in clj? conveyance might be one complication, are there more?
<rlb>If (and only if) you want to poke at it, this should give a start: https://clojure.org/reference/vars "Vars" ar much like guile's variables (i.e. an indirection), though everything's thread-safe, and they can be marked "dynamic" in which case they have the behaviors mentioned there.
<rlb>"are much"
<rlb>(it's mostly the first two sections there)
<rlb>Also note that with-redefs* is intended to be "rare" (can cause a mess -- I've most often seen it used in tests to redefine functions during the test because it'd be "too much" work to "do it right").
<rlb>(But then you've quite possibly made the tests unparallelizable.)