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 () <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>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, 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). <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 <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>(which is different than returning unspecified) <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>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 <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). <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>(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) <old>so I can finally configure emacs with Scheme instead of horrible elisp? <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 <robin>oof. i hadn't actually looks for counterexamples in the emacs source :/ <mwette>release notes for emacs-28 stated everything was lexical IIRc <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 <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 <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 <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>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? <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>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>(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.)