IRC channel logs

2023-09-24.log

back to list of logs

<oriansj>muurkha: one could argue that is the same weakness of scheme macros. The temptation of being clever can easily result in code that difficult (if not impossible for others to understand)
<muurkha>oriansj: yes, and even more so, non-Scheme macros; Scheme's macro systems are deliberately very constrained to limit the risk
<oriansj>well simple macros are a very powerful tool and I do agree very constrained macros is a great idea.
<oriansj>but when one has a good compiled s-expression language, I don't see any reason to have macros
<muurkha>macros are the simplest way to implement a lot of kinds of syntactic sugar
<muurkha>like Scheme's (define (raskage glaud1 glaud2) ...) for (define raskage (lambda (glaud1 glaud2) ...))
<muurkha>or its (and a b) for (let ((newvar a)) (if a a b)) and analogously for or
<notgull>Being clever is good if you're the only one working on the project. Unfortunately that is pretty rarely the case
<muurkha>and similarly for variadic and, or, addition, etc.
<notgull>Same reason why people hate preprcessor macros in C
<muurkha>People hate preprocessor macros in C largely because they're bug-prone and unreliable. Scheme macros do not have those problems, and they prevent you from having to be clever, allowing you to use a simple macro definition for many constructs instead of a much more complex compiler case.
<muurkha>Being clever is not good if you're the only one working on the project, either, if working code is your objective: "Debugging is twice as hard as writing the code in the first place, so if you write the code as cleverly as possible, you will not be smart enough to debug it."
<muurkha>Even Common Lisp macros, which are much less constrained than syntax-rules (the R⁵RS macro system), do not suffer the pervasive unreliability problems of C preprocessor macros
<notgull>True
<muurkha>The thing that most reduces my appetite for Lisp macros is lightweight lambda syntax, like in Smalltalk, Ruby, and recent versions of JS
<muurkha>in http://canonical.org/~kragen/sw/dev3/qvaders it allows me to write things like
<muurkha> vaders.each(v => {v.Δx = -v.Δx; v.y += 8})
<muurkha>if (vaders.where(({x}) => x < 0).count()) vaders.each(vader => vader.Δx += .5)
<muurkha> bombs.outside(screen).each(b => {blam(b, .8); b.die()})
<muurkha>that's not quite as syntactically lightweight as it would be in Smalltalk but it's a great deal better than writing vaders.each(function(vader) { vader.Δx += .5 })
<muurkha>or (each vaders (lambda (vader) (incf (Δx vader) .5)))
<notgull>Lambdas are pretty nice
<muurkha>which results in a strong temptation to write a macro that allows you to write it as
<muurkha>(each vader vaders (incf (Δx vader) .5))
<muurkha>the objective is a clear expression of the ideas of the game (or compiler or whatever) with a minimum of syntactic noise obscuring the signal. dynamic typing often helps with this, but it can hurt by making it harder to figure out what you're looking at
<muurkha>S-expression syntax also has some real disadvantages on that part; (x y) in different contexts can be an assignment of y to x, an invocation of x with y as an argument, a condition-action pair in which y is evaluated if x is true, a list of the value of x and the value of y (for example in Scheme `case`), or a list of the two literal symbols 'x and 'y. Larry Wall said, "Lisp has all the visual
<muurkha>appeal of oatmeal with fingernail clippings mixed in."
<muurkha>and often you end up with more syntactic tokens than in infix syntaxes, as in my incf example here, which is 8 tokens in Common Lisp but 5 tokens in JS, even without the added inconvenience that the whitespace in the Lisp is semantically meaningful
<muurkha>s/on that part/on that count/
<muurkha>But there are a lot of things you can do with macros but not with lightweight lambda syntax.
<oriansj>muurkha: excellently put
<lilyp>muurkha: coming from C++, that lambda does look like line noise to me, though :)
<lilyp>btw. we do have stuff like cut so (for-each (cut (incf Δx) <> .5) vaders) would work as well, provided you generalize incf for getters with setters
<muurkha>lilyp: point-free programming usually leads to terser code but I have not yet learned to find it readable. where does cut come from? I'm not familiar with for-each, cut, and <>
<lilyp>cut is from SRFI 26
<muurkha>aha, thanks
<lilyp>it's your best friend when you want to do (lambda (x) (f x y)) or the like
<muurkha>aha, so it's not really point-free
<muurkha><> is a point
<muurkha>thanks for turning me on to SRFI 26!
<janneke>fwiw, in most every case cut can be replaced by cute
<lilyp>true, but sometimes the compiler doesn't like you when you do that
<lilyp>(warning: potentially wrong number of arguments …)
<muurkha>it's unfortunate that (cut if <> 0 1) is illegal
<muurkha>that's sort of in the same direction as (cut + (* <> 3) 1) though (meaning (lambda (m) (+ (* m 3) 1))) which is also illegal
<muurkha>is there a SRFI for setf, incf, etc.?
<muurkha>oriansj: https://www.righto.com/2020/11/reverse-engineering-classic-mk4116-16.html is an example of a popular 1-bit-wide memory chip from the 70s
<muurkha>my memory is that this kind of thing was the normal way to do things at least until the end of the 36-pin DIMM age
<muurkha>it makes sense that nobody is selling 1-bit-wide 16-kilobit SRAM or DRAM chips today; fabrication densities are much higher, and if you need 16 kilobytes of memory, it makes more sense to get it on a single chip with 8 data lines
<muurkha>or more, like 16, 21, 32, 64, or 72
<muurkha>but when you're pushing the fabrication technology's limits, especially when clock skew is no worry, it makes sense to gang up 8 or 16 chips on a single address bus and set of control signals, each connected to one bit of the data bus