5 ms·
This is exactly what I'm talking about. Go is a statically typed C-like compiled language. It can convert the code much or less in its entirety to machine code.
by terminalcommand 4y ago
This is exactly what I'm talking about. Go is a statically typed C-like compiled language. It can convert the code much or less in its entirety to machine code. Lisp and forth on the other hand cannot, I think. They need some kind of interpretation at runtime to function so dynamically. If this is not the case, please prove me wrong. The distinction between compile time vs run time bugs me a lot.
I think it is easier to say forth is closer to compiled. I can kind of imagine each user defined word being translated into assembly. With lisp I am speechles.
- Jtsummers 4y agoLisp can be ahead of time compiled, being dynamically typed doesn't impact that. It does impact what gets generated by the compiler. Compiled CL code is usually "generic", it has logic inside that helps it dispatch based on the dynamic types during the runtime but this is not interpretation (in the sense meant by TCL, Python, and others). You can also specify the types and a compiler can, optionally, make (or use) more specific code which should error on a type mismatch but can run faster if you give it the correct, restricted types. As to your speechlessness regarding compiled Lisp, here's a quick example: CL-USER> (defun foo (n) (+ 1 n)) FOO CL-USER> (disassemble #'foo) ; disassembly for FOO ; Size: 35 bytes. Origin: #x53641724 ; FOO ; 24: 498B4510 MOV RAX, [R13+16] ; thread.binding-stack-pointer ; 28: 488945F8 MOV [RBP-8], RAX ; 2C: BF02000000 MOV EDI, 2 ; 31: 488BD3 MOV RDX, RBX ; 34: FF14250001A052 CALL QWORD PTR [#x52A00100] ; SB-VM::GENERIC-+ ; 3B: 488B5DF0 MOV RBX, [RBP-16] ; 3F: 488BE5 MOV RSP, RBP ; 42: F8 CLC ; 43: 5D POP RBP ; 44: C3 RET ; 45: CC10 INT3 16 ; Invalid argument count trap NIL CL-USER> (SBCL was used for the above)
- terminalcommand 4y agoI know that lisp compilers compile compute heavy trivial functions directly to machine code. But how is the output of a program containing a lisp macro for example. Let's say define a lisp macro don't call it and generate its assembly. What is the machine code output? This is the part I'm speechless about. "The logic inside that helps dispatch based on the dynamic types at runtime" is the interpreter part IMHO. Plus you need logic to add the metaprogramming elements that require you to change the code after it has been written. We need to generate an example of something we can't do in C, something which requires evaluation at runtime.
- Jtsummers 4y ago> But how is the output of a program containing a lisp macro for example. I think you have a fundamental confusion about when and where macros are applied. In a compiled CL implementation, macros are expanded prior to compilation. The code will be exactly the same as if there was no macro involved. If you want to see what that might look like take the function body of this simple function: (defun bar (filename) (with-open-file (in filename :element-type '(unsigned-byte 16)) (let ((contents (make-array (file-length in) :element-type '(unsigned-byte 16)))) (read-sequence contents in) contents))) And run it through `macroexpand` (`with-open-file` is the macro I'm particularly interested in here): CL-USER> (macroexpand '(with-open-file (in filename :element-type '(unsigned-byte 16)) (let ((contents (make-array (file-length in) :element-type '(unsigned-byte 16)))) (read-sequence contents in) contents))) (LET ((IN (OPEN FILENAME :ELEMENT-TYPE '(UNSIGNED-BYTE 16))) (#:G659 T)) (UNWIND-PROTECT (MULTIPLE-VALUE-PROG1 (PROGN (LET ((CONTENTS (MAKE-ARRAY (FILE-LENGTH IN) :ELEMENT-TYPE '(UNSIGNED-BYTE 16)))) (READ-SEQUENCE CONTENTS IN) CONTENTS)) (SETQ #:G659 NIL)) (WHEN IN (CLOSE IN :ABORT #:G659)))) Now take that output and put it in a new function, call it `baz`. Then disassemble both `bar` and `baz` (I've done this and included the output of just one because they have the same instructions): EDIT: Snipped the excessively long code, here's a link: https://topaz.github.io/paste/#XQAAAQCODwAAAAAAAAAQYOhAaDnr7QIy+t3L74P+gfSyurN4xBqqRWDKGrfTibEvWKLiZQV6OUDskNamSTE6rKL6Tc41DobwEH/u5gDW2L0lA3v35/1zJ1y6GYXeZrrRB2PfLaksX/d61C9WtCwQuB5kdoZtgD9tkQ1wcJKGC9apdT06udkLusjg3ZfQW+q3+gMlY2UIRcia9P9Jpe0Do089yFEpmZzwcbQjWDhbnTXUG/Jg6leXLrMbqZ0XYJHSgDHy4s8MoIiXPsBHl7K8UG7qQu1LBVFgVApFwzld1lLUqk1/c8+Wh4oVR6nwPc52ddWb/25+p1N5x4RQtrn/iG2U+BgJcjma/XfNVgJVbhZ82rceK9Dlg4zGkDcnDpqfapy7YocJ4f5YXkLrdGYjK2LoWxkZwjGYIGvlFfJCB1MqZbmJLxrHAgCOe+F7VjQE39Mh72bEZjv2rTQKW0/P5Vsg4JcLM2j5/vJRnszz3gTm7+3oxxg/vJ+58JB/1dIwZiXixlGTDm2Sax7u6ykOy9kTtOdqzTptzlcq74XOIjTut4DhWc5GEB6HVjpU8sVMAtmmdu1GdAT3yfiuPcHIoAaKv2FD1nTtDzYx36if8AXQfUr1XeoxYupiA5BfM7nsmVN7cJhutV1rRNEgybudHoG9RDwoCFkIKDsH2Ba2aIzNLpkwS6H6JNNMUrvQ+gZY3FLLPLkhg5/YCAgRjKfv23lg72f8APMQMMW36C5KlcmIpvl9K0JenkXg6AEx1Fqou6vIPXNMskYhCh7eaPGIH6Pj9hrGEF35d+OPKD4U3tn6zKdMElcltcOfodsKUeGOiS+fGmd8VVtQpgymz+0BX6vNB4wIElQukdRd/Ksv/7WOVThauhUMis8hJYeOrfNV838h1x0zcdiVBuMsdTLDo5HwvOfO0BkbgwAzkVT93xWd+HGYKSMv5Z+anFVpYMnw+icd8d5bSvEGOLDkHh96FIvVrH9GB49PQIOOkeJJ+MOgDyqz8SuuRlHOTbep+a3VibYKf80zgcFOcUu03eORCKp56Fxrs3fVgx5xn/TYgzhDw60XpsaPBSL9nobVpJH1e0W2Rx6HedSHhg1ZgGsr20BJB8xPw1/qtjCErimpZzCkWdcF9R2XkSU2qXaEWDGz9u6Wrhr9Jc1tu8W1721kgMSRBlcuvS32V7n5CsNmv1aRyC6KENw71+mLekuJGXbbWqtzIOd05gFUu7PJhDOcAPbUTcAQeaQY3vu0C9LcGM/byfbexPmj957XRo1Bk5OfW73TJsj45w3LNjFeGKN2HuX28UaleHnilNz5OaI5JSs3JYE7LFw//2tYYQA= https://topaz.github.io/paste/#XQAAAQCODwAAAAAAAAAQYOhAaDnr7... (still long, but at least HN will truncate it) It doesn't really make sense to talk about disassembling a macro as a macro is not, itself, what gets compiled when it's applied, but rather its output gets compiled (in a compiled Lisp). > "The logic inside that helps dispatch based on the dynamic types at runtime" is the interpreter part IMHO. Then every compiled program that uses a tagged union is really an interpreted program (which is a statement you could make a strong argument for, perhaps). The code above is still machine code, though, it's not being interpreted. This is part of the distinction between an interpreter and a runtime. Dynamic dispatch can be applied to statically typed languages as well as dynamically typed languages. > We need to generate an example of something we can't do in C, something which requires evaluation at runtime. Strictly speaking, there is no such thing as something that can be done in Lisp that cannot be done in C. It'll just take a lot more effort in C to do some things like compiling a function on the fly, returning it, and then applying it later. You'd at least need to link in a compiler and feed it some structure which it can then compile into a function which gets returned, probably, as a function pointer.