5 ms·
> It's a way to modify a code base without changing the source files Is another way of saying your source files might not do what they say! You're not selling
by jstanley 2y ago
> It's a way to modify a code base without changing the source files
Is another way of saying your source files might not do what they say! You're not selling it to me.
- pfdietz 2y agoA use case is code coverage: you can instrument your code by compiling it in a dynamic environment where the hook causes extra coverage recording code to be inserted. You'd like to be able to do this without changing the source code itself.
- diffxx 2y agohttps://www.cs.cmu.edu/~rdriley/487/papers/Thompson_1984_ReflectionsonTrustingTrust.pdf https://www.cs.cmu.edu/~rdriley/487/papers/Thompson_1984_Ref...
- mst 2y agoWrapping the existing code in hooks of some sort to enable injecting profiling or whatever is really quite nice. A lot of javascript hot reloading implementations rely on using Babel to do that as part of a build step so they output the wrapped code and your app loads that instead (and it's not that dissimilar to compilers having a debug mode where they output very different code). I would certainly side-eye you for using the relevant common lisp hooks in production without a very convincing reason, but for many development time tasks it's basically the civilised version of widely used techniques.
- praptak 2y ago> your source files might not do what they say! The disconnect between what your source file "does" and what it "says" is present in everything above assembly. Change implementation of `foo(...)` and suddenly dozens of source files (none of which is modified!) which make calls like `foo(x)`, `foo(y)` and `foo(y+1)` change what they "do", all this without changing a single line in these files.
- jerf 2y agoThere is still a qualitative difference between reading x.y = foo(z, t) reading as "there is a variable labelled x, with a defined type within this scope, that contains a struct member y with a statically-computable offset within x's known memory layout, that will receive a copy of the function foo (which is known at compile time), which is the result of calling foo with a copy of z and t, which are also values with compile-time known types and layouts", even if you as a human may have to look up foo to know what is going on, and the equivalent in Python, which I will break out into a separate paragraph because this is going to get long even though it will still be incomplete: "there is a reference x to an unknown type, which we will call one of several methods to resolve a field called y, where each of these methods may have been dynamically modified by previous code to not match their source implementation, which may either be a concrete value or a "descriptor", which will will have some method call performed on it (with full OO resolution rules on the method call, including the ability for the class structure to have been dynamically modified by code at run time and also all methods in the class structure to have been dynamically modified, and also the method can do anything a method can do including its own further dynamic modifications to arbitrary other structures, and also the result of running class and method decorators on everything in this hierarchy) with the result of calling foo, a values of unknown type that may be a function, method, or arbitrary values with a __call__ value (which itself may perform arbitrary dynamic operations in the process of returning a callable value), with copies of a reference to the values z and t of unknown type and capability, which will be filled in to foo's argument list in additional possibly complicated manners based on foo's definition and whether it uses args or kwargs, and also all of this code including the very statement under question may not exist in source at all because it may have been 100% dynamically constructed at run time itself." And I probably missed some things in the Python too, but even so, Python already has numerous "and actually literally arbitrary amounts of code could be running here", and can readily introduce more. All languages have the issues where you might have to resolve a function call as a human to know what is is doing, but in some languages, you're nearly done after that (C, Go) and in some languages your journey is just beginning (C++, Python).
- praptak 2y agoSo Lisp, even with macros, is not that bad. Macros add just one mechanism which you have to know in order to understand a piece of Lisp code from first principles. The number of other special mechanisms in Lisp is much lower than in Python or C++. Obviously macros are powerful which is another way to say "footgun". Also it makes the comparison a bit unfair - an out-of-the-box Lisp usually already has many macros which you have to know. They provide some of the stuff which needs to be hard coded into the compilers in languages like C++ and Python.