3 ms·
Right, advising itself on the surface doesn't seem to be inherently tied to homoiconicity, here's the concrete example in Elisp, this would print how long it to
by iLemming 2y ago
Right, advising itself on the surface doesn't seem to be inherently tied to homoiconicity, here's the concrete example in Elisp, this would print how long it took to evaluate a form.
(defun add-timing-advice (orig-fun &rest args)
(let ((start-time (current-time)))
(prog1 (apply orig-fun args)
(message "Evaluation took %f seconds"
(float-time (time-subtract (current-time) start-time))))))
(advice-add 'eval-print-last-sexp :around #'add-timing-advice)
While advising isn't inherently tied to homoiconicity, it is significantly easier to implement and more powerful in a homoiconic language.
Here's a different (not related to advising) example of redefining core macro, introducing code that "writes code":
(defmacro lambda (args &rest body)
`(function (lambda ,args
(print "Lambda called!")
,@body)))
Now every lambda expression will print a message when called.
To implement something like that in Python, you'd have to modify the parser and complier, alter the runtime, reimplement core language constructs, but most importantly, you will have to change Python's syntax to make all code easily representable as data structures. Which then will become not Python but entirely different language.
- School-Cotton 2y agoYour lambda example is a fair point, but will that apply to things that have already been byte-compiled?
- iLemming 2y agoI'm not exactly sure what you mean, tbh.
- School-Cotton 2y agoI mean, if a function that uses lambda has already been parsed and compiled, I’m guessing it won’t magically be updated to use the new definition of lambda. Not sure if I’m correct about that.
- iLemming 2y agoIn this case it would't care. Since it's basically redefining what (lambda) means to the Reader. That is where that notorious distinction of Read-Eval-Print-Loop comes to play. 1. Read: - In Python, the read phase must parse text into an intermediate representation (AST) that is distinct from Python's data structures. During the read phase, Python using built-in tokenizer and parser, parses its infamous indent-based syntax, handles literals, identifiers, keywords, operators, decorators, docstrings, comments, etc. Finally, it builds an AST. - In Lisp, the read phase produces a data structure (s-expressions) that is directly usable as code. The syntax and the abstract syntax tree are essentially the same thing. Lisp uses built-in Reader - parses s-expressions while handling special syntax elements - quotes, backticks, commas, hash-quotes, handles reader macros, parses numbers strings, other literals, comments. But! No need to build an AST - the code already is. 2. Eval: - Lisp can evaluate the s-expressions directly. The code is data, and data is code. That's where macros get expanded before the evaluation, function calls are resolved, symbols are looked up, tail calls optimized, byte-code compilation is available but not mandatory. - Python must compile its AST into bytecode before execution. Python uses stack-based VM, figures out scoping and class namespaces, special forms (if, for, def) handled by specific bytecode instructions. Decorators are applied, reference counting and GC, tail call optimization (if runtime has it, standard CPython doesn't). It always has to compile bytecode before execution. - Lisp macros operate on the code structure directly, allowing for powerful metaprogramming. Python's metaclasses and decorators are more limited in comparison. 3. Print: - Elisp's printed representation of data is often directly readable as code. What you see is what you can evaluate. Eisp has built-in circular structure handling, Python doesn't. Elisp's printing is more focused on producing readable/evaluable Lisp expressions - Python's printed representation, especially for complex objects, is often not directly executable code. Python's object-oriented approach allows for more customization through methods 4. Loop: The Loop phase in both cases ensures that the REPL continues to accept and process input, making interactive development and experimentation possible. - In Lisp, you can easily manipulate the environment, even the REPL itself, using the same language constructs. - Python's REPL is more of a black box from the language's perspective.
- lispm 2y ago