On Thu, Jun 24, 2010 at 1:55 AM, Ken Raeburn <[email protected]> wrote: > On Jun 23, 2010, at 17:09, Michael Lucy wrote: >> Is there any scheme expression that will just get ignored when the >> scheme code is compiled? >> >> I'm generating some code with a function like: >> >> (define (gen-update-ab updatea updateb) >> `(begin >> ,(if updatea `(set! a (+ a 1)) `(donothing)) >> ,(if updateb `(set! b (+ b 1)) `(donothing)))) >> >> And ideally I could replace the donothing function with something that >> will get discarded during compilation. > > A simple constant like #f or '() or 42 shouldn't cause any evaluation to > happen, unless it's the last expression and thus the value to be returned (in > which case you'd just be returning a simple constant).
Ah, I see. I tried that with variables, and just assumed that if they weren't optimized out constants wouldn't be either (in retrospect not such a great assumption): scheme@(guile-user)> ,c (begin a b 1) Disassembly of #<objcode d123d28>: 0 (assert-nargs-ee/locals 0) 2 (load-symbol "a") ;; a 7 (link-now) 8 (variable-ref) 9 (drop) 10 (load-symbol "b") ;; b 15 (link-now) 16 (variable-ref) 17 (drop) 18 (make-int8:1) ;; 1 19 (return) Out of curiosity, why are variables left in? Are there situations where evaluating a variable will have side-effects? Anyway, thanks! That makes life a lot simpler. > > I don't know if there's a canonical efficient way to generate it, but it > looks like "(if #f #f)" will be optimized by the current compiler into just > pushing the magic "undefined value" onto the stack. Getting rid of the push > altogether *IF* the result of the expression is unused is up to the > optimizer; you shouldn't be jumping through hoops in the code you generate to > make that happen. But it appears that it does happen at least in simple > cases: > > scheme@(guile-user)> ,c (begin (if #f #f) (if #f #f) 42) > Disassembly of #<objcode 101678308>: > > 0 (assert-nargs-ee/locals 0) > 2 (make-int8 42) ;; 42 > 4 (return) > > Hmm... here's another way, though I've no idea if RnRS lets you not have any > expressions in here: > > scheme@(guile-user)> ,c (begin) > Disassembly of #<objcode 10163f848>: > > 0 (assert-nargs-ee/locals 0) > 2 (void) > 3 (return) > > scheme@(guile-user)> ,c (begin (begin) (begin) 42) > Disassembly of #<objcode 1016782a8>: > > 0 (assert-nargs-ee/locals 0) > 2 (make-int8 42) ;; 42 > 4 (return) > > >> There are alternatives, but they're pretty ugly (significantly moreso >> in my actual code than they look below): > > Depends how you define ugliness. :-) > > Your approach expands each possible update into one S-expression. So you > need *something* there for each one, even if it's just a constant, or an > empty "begin". > > The alternative, I think, is rearranging your code so that no expression gets > added to the list when there's nothing to do; one way to do that, off the top > of my head, would be to generate a possibly-empty list of expressions rather > than exactly one for each possible update, and then merge the lists together. > Or start with an empty list, and tack S-expressions on the front for updates > that are needed, then add the "begin" and return the result. > > But in terms of the generated byte code, the constants should just go away if > they're not actually used, and nested "begin" lists should get flattened, so > I don't think it should matter much either way. > > If you want the macro expansion to be human-readable for debugging purposes, > you could also consider putting in some kind of no-op annotation that > shouldn't generate code, for example a let loop with a meaningful label name > and no useful body, or wrapping code that actually has effects but doesn't > use the label: > > scheme@(guile-user)> ,c (begin (let no-update-needed-for-a () #f) 42) > Disassembly of #<objcode 1030dc468>: > > 0 (assert-nargs-ee/locals 0) > 2 (br :L171) ;; -> 10 > 6 (br :L172) ;; -> 14 > 10 (br :L173) ;; -> 6 > 14 (make-int8 42) ;; 42 > 16 (return) > > Ehh... okay, maybe there's a little work to be done for that one. :-) > > Ken
