24 comments

[ 3.5 ms ] story [ 44.8 ms ] thread
Ah, self-modifying code, the more things change the more they stay the same.

Wasn't SMC one of the LISP-associated AI fields a few decades ago? iirc it's been mostly abandoned due to security issues, but some of it survives in dynamic compilation.

Aka I just learned how to modify python code whilst it's running.

Next step, here's how to load modules, resolve a dependency. Handle capabilities and dynamically inject more functionality 'live'.

Patching running machine code in memory for compiled objects is the same but you just need to work around the abstraction that is introduced by languages trying to make the whole stack human parseable.

And wouldn't it be nice if that Python code, instead of a string, was just more python? Then you could use your existing Python code to append, or transform sections of your code!

That's what Lisp is!

Once you see how cool that is, then you can begin to appreciate why Lisp was the defacto standard for AI programing all the way back in the 1960s!

Imagine if you've lisp to make ide and in a closed loop integration with LLM which extends and tests this IDE to achieve the task at hand.
I really couldn't follow the use case. it looked like, they have a chain of method calls with some kind of `mark_circle().encode().properties()`, OK, so, if you want to make those methods do something different, you reach into OOP wisdom from the 1980s and write an appropriate "impl" used by whatever `alt.Chart()` is.

Someone explain to me, an old, aging programmer old enough to know UML, why this isn't some we presume very young person who has no idea how to write OOP coming up with some horrible convoluted way to do something routine?

Yeah, I've been using this trick to implement a hot reload library, to only update the specific functions that are changed without re-executing modules: https://github.com/breuleux/jurigged

I also use it in a multiple dispatch library (https://github.com/breuleux/ovld) to replace the entry point by specialized dispatch code in order to cut some overhead.

It's fun.

Also, why is every damn post these days somehow framed in an AI context? It's exhausting.

I used live patching of the function byte code to enforce type safety in python as an experiment. It was quite fun, took about a weekend or so :) not something for production though, due to the performance hit.
There is a reason similar approaches are called 'monkey patching'.

Just cause you can do something doesn't mean you should. I send thoughts and prayers for the people debugging programs where this is in place.

Python being riddled with security anti-patterns -- or at least security-unfriendly ergonomics -- is one reason I tried to stop working with it.
wait a sec ... i thought you could monkey patch python code. you can only do it by using this technique?
Other than Lisp, is it possible to do this in other languages?
I'm not sure what the point of this blogpost was. As far as I can tell, the author discovered eval() but is making it more complicated for no reason? There also isn't any actual patching going on.
One of the problem with dynamically loaded code like this is when you raise exceptions in those functions - the traceback will then end with something like `File "<magic>", line 8, in something`, which will at least annoy you when debugging.
Monkey patching python modules is waaaaay more straightforward than this.

setattr(mod, name, new_func)

I've contemplated patching Python's asyncio package so that the list of funcs isn't a weakref... but it's not that hard! This is way harder than it needs to be.
I did this by accident in bash scripts many years ago when I was just getting into linux. I'd be running a script, and editing the script at the same time. It caused some REALLY weird issues before I figured out what was happening. For instance I'd change the text somewhere and it would change in the running program, or the program would get into states it should never be in. I didn't use it constructively, I just avoided editing running programs after that.
Neat trick indeed, but I have not yet found a compelling use-case for this that can't be done more sensibly using lambdas.

I once inherited a nice library which made heavy use of such compile/exec pairs. The code looked very convoluted and was very bug prone. I replaced all such pairs with lambda wrappers. This was much more readable: my colleagues who were also working on this code were like "oh is that what that was meant to be doing" after this change.

The use case is not that obvious to me, but I _think_ it's trying to give the LLM more context to write code by dipping into these code objects. Perhaps telling the LLM what modules are already imported or otherwise available?

Creating a debugger for an LLM (not a human) is something I haven't really seen, but seems super useful...?