Over the last few years I've been occasionally testing Gemini's ability to write Dylan code. Dylan has such a small user community and code base I'm not usually surprised at the results.

That said, it has progressed quite a lot since a few years ago from "Dylan is basically Python, right?" (nope) to "here's some base64 Dylan code that is almost correct". Yesterday I asked it to convert this CL macro

(defmacro inc! (place) `(setf ,place (+ 1 ,place)))

to Dylan and was fully expecting it to generate a mess. Instead it did it perfectly (see image).

#DylanLang #CommonLisp

(I post this with some trepidation because I know AI generates a lot of strong feelings. I don't really want to get into that debate and I'll just say this is the only way I've used AI at all, just as a curiosity in relation to my Dylan hobby.)

Double evaluation is a good point but it's beyond the scope of this test. I'm fine with assuming people won't write

inc!(side-effecty-thing(...))
or
inc!(slot-getter(side-effecty-thing(...)))

99.9% of the time it'll be inc!(var) or inc!(slot-getter(var)) which are both fine.

No, Dylan has nothing like define-modify-macro.

TBH I never used define-modify-macro in my CL career and from https://cl.opendylan.org/define-modify-macro I'm not sure quite what it would look like in Dylan. The only thing Dylan provides that is similar to SETF is a simple rule:

f(...) := expression

is exactly equivalent to

f-setter(expression, ...)

The canonical case being slots that are settable have a -setter function and constant slots don't.

I was being facetious, but seriously, avoiding double evaluation is a lesser benefit of a modify macro.
A greater benefit is that it allows modification of a non-existent place, but one for which a default value can be supplied.
The usual example is with a hash table:

(let ((h (make-hash-table)))
(incf (gethash 'foo h 0))
(gethash 'foo h))
=> 1

I need this kind of thing every so often.

Otherwise, it is true that `define-modify-macro' itself is rarely needed, because the usual things are already provided.
I think I have used it once, maybe twice, perhaps to write `appendf' or something like that.

***

«The only thing Dylan provides that is similar to SETF is a simple rule:

f(...) := expression

is exactly equivalent to

f-setter(expression, ...)»

Thank you.

#CommonLisp
#Dylan

@sigue

@vnikolov

(BTW, I don't want you to get the feeling that I'm making this a competition between CL and Dylan here. I actually really enjoy that you're making me think about how I might do some of this stuff in Dylan.)

«(let ((h (make-hash-table)))
(incf (gethash 'foo h 0))
(gethash 'foo h))
=> 1»

This specific example is pretty straight-forward in Dylan:

let t = make(<table>);
t[#"foo"] := 1 + element(t, #"foo", default: 0);
// => 1

So I doubt it's worth the trouble to improve it, but if we wanted to what might it look like?

t[k] is just syntactic sugar for element(t, k) and earlier I mentioned that := is also syntactic sugar, so t[k] := v is element-setter(v, t, k)

In theory we could add `#key default` to the element-setter generic function and then this would work:

inc!(element(t, #"foo", default: 0));

One could also imagine syntax to supply a default value to t[k] such as t[k; d], and then this would work:

inc!(t[#"foo"; 0])

But all this effort would just be for element and element-setter, whereas the Common Lisp generalized place mechanism is, well, generalized.

I looked in the SBCL sources for define-modify-macro and define-setf-expander and there aren't many. The most interesting one is perhaps (setf values). A while ago I wrote up a proposal for an extension to Dylan that would provide similar functionality in https://opendylan.org/proposals/dep-0013-multi-assignment.html I think it needs work but it gets the idea across.

One might point out that the awesome thing about CL is that this can be done in-language instead of needing to modify the compiler. :)

@sigue wrote:

«BTW, I don't want you to get the feeling that I'm making this a competition between CL and Dylan here. I actually really enjoy that you're making me think about how I might do some of this stuff in Dylan.»

Oh, I did not and do not view this as a competition, not at all.
And I am glad to learn a little bit about Dylan.

«This specific example is pretty straight-forward in Dylan:

let t = make(<table>);
t[#"foo"] := 1 + element(t, #"foo", default: 0);
// => 1»

Thank you.
Yes, that is how I do it in Python, too, when I need it (there are some minor syntactic differences, of course):
t['foo'] = 1 + t.get('foo', 0)

«In theory we could add `#key default` to the element-setter generic function and then this would work:

inc!(element(t, #"foo", default: 0));

One could also imagine syntax to supply a default value to t[k] such as t[k; d], and then this would work:

inc!(t[#"foo"; 0])»

Thank you; at the very least this is an interesting little exercise.

«A while ago I wrote up a proposal for an extension to Dylan that would provide similar functionality in https://opendylan.org/proposals/dep-0013-multi-assignment.html I think it needs work but it gets the idea across.»

I'm sure this is fun to work on.
Otherwise, I am just a side observer here, of course (regarding justification, acceptance, etc.).

«One might point out that the awesome thing about CL is that this can be done in-language instead of needing to modify the compiler. :)»

Oh, yes, oh, yes!

#CommonLisp
#Dylan