4 ms·
I think an example would be helpful, conditionals: Within the "program dimension" there is just no way to run code conditionally without an if, no matter how m
by eddd-ddde 2y ago
I think an example would be helpful, conditionals:
Within the "program dimension" there is just no way to run code conditionally without an if, no matter how much you move left and right, you are constrained. It is only possible by using the "higher dimension".
- jcul 2y agoThis is different from constexpr if in C++? Where one branch will not exist in the compiled code?
- Jtsummers 2y agoLisp macros take zero [0] or more unevaluated forms, does something with them (which may be computing a value, see the factorial example elsewhere in this discussion and the submitted blog), and then returns a new form (which could be that computed value, again see the factorial example) which the Lisp system evaluates eventually. One way to see this would be to do something like: (defmacro print-value (a) (print a) a) (print (print-value 1)) (print (print-value (+ 1 2)) ;; output 1 1 (+ 1 2) 3 https://tio.run/##S87JLC74/18jJTUtNzG5KF@hoCgzr0S3LDGnNFVBI1FTQQMsoABkJWpycUF5GsiqDDU1sYpraCsYKhhpamr@/w8A https://tio.run/##S87JLC74/18jJTUtNzG5KF@hoCgzr0S3LDGnNFVBI1... The value bound to a inside the macro is the unevaluated form (the first and third items printed out). In this case the macro itself is just the identity macro other than its print effect so it returns the original form, which is why we get 1 and 3 printed as well. Here's an example of processing (very primitive) the unevaluated form to produce something new: (defmacro infix (a op b) `(,op ,a ,b)) ;; alternatively: (list op a b) (print (infix 10 + 20)) ;; output 30 Now we can get fancy (this is primitive still, but works): https://tio.run/##fZDBDoMgEETvfsXculh7qP2hImJKSpUgSf17uiqJ1Vr3QMjbmVkWZU3vYqRaNy@pfAfTNmYA6cF5kQFkZaVtD6K3tM@Fb4pU19a/mBvEA4LD5MOdCur1qE2gSLGN8X2Y2QLDw/gk5NpND@v2bEwoI@dNG0DTYgwljz1RWrBEjpv4lm0ax3aJM6qV3Wo@mF8FqELJe/zX7kayLocSLGb/hW@H8fzl4zNx6I7xAw https://tio.run/##fZDBDoMgEETvfsXculh7qP2hImJKSpUgSf17uiqJ1V... (defmacro infix (expr) (labels ((walk (expr) (cond ((listp expr) `(,(second expr) ,(walk (first expr)) ,(walk (third expr)))) (t expr)))) (walk expr))) This generates a valid (prefix notation) lisp form from a more traditional infix form from mathematics. If I were being more clever I wouldn't use the second item in the list (the operation) directly but restrict it to valid arithmetic operators and change how I walk the structure. That would remove the need to force explicit parentheses since I could add in a proper parsing step. This is a very primitive version of what loop and other complex macros do. They take essentially a different language, parse it, and emit a valid Lisp form which is then evaluated. You could use this to get constexpr like behavior but once you do that you run into problems, you can't do this for example: (defmacro foo (a b) (+ a b)) (foo (+ 1 2) (+ 3 4)) ;; error (let ((a 1) (b 2)) (foo a b)) ;; error (foo 1 2) ;; => 3 It only partially works because it only works, when a and b are both numbers. If they're symbols (second case) or other forms (first case) then the macro attempts to compute something that cannot be computed. You can fix the first case by doing: (defmacro foo (a b) (+ (eval a) (eval b)) But that still leaves the second case erroring out. You could do something like what I did with infix which walks the forms and determines if they can be evaluated (no unbound variables) and then evaluate them conditionally, leaving expressions with unbound symbols intact to be processed later. So C++ constexprs are less than Lisp macros, but if you want to use Lisp macros to do the same thing as constexprs you have to do more work. Check out On Lisp and Let Over Lambda for two books that go deep into macros in Common Lisp. [0] I honestly don't know why you'd want to do this, but technically it's valid to do: (defmacro foo () (some form to return)) (macroexpand '(foo)) ;; => (some form to return) I cannot think of a case where this would be useful, but someone else might think of one.
- nightowl_games 2y ago"move left and right"? This meant nothing to me. 'higher dimension' Still pretty abstract.
- xmcqdpt2 2y agoThat’s not that great of an example because you can do much the same with closures, which are seamless in some languages. In Scala for example, def cond[T](p: Boolean, a: => T, b: => T): T = if (p) a else b will only evaluate one of the two arguments. https://docs.scala-lang.org/tour/by-name-parameters.html https://docs.scala-lang.org/tour/by-name-parameters.html You can do the same in Java with more syntax. The real power of Lisp macro is that you introspect the code and modify it, not that you can alter evaluation eagerness.