-
-
Save commander-trashdin/74e200c22326f3835c8e8d959495c4d8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Python (3) to (Common) Lisp feature comparison | |
Memory model & typing: | |
CL & Python are both managed-memory languages. | |
Both are also dynamically-typed languages with strong type systems (no implicit type coercion), although Python's design leans toward "duck" typing, and CL's does not. | |
Syntax: | |
CL syntax has all code in s-expression format, where a form is a space-delimited list bounded by parentheses with the name at the beginning: (name arg1 arg2 ...) | |
Python syntax is very distantly related to C, with major differences such as semantically-significant whitespace and use of said whitespace to delimit blocks. | |
CL function call syntax is (name arg1 arg2 arg3 ...). Positional, optional, keyword, and "rest" (e.g. "you can pass in an arbitrary number of arguments to this function and they all get bundled into a list and shoved in this parameter) arguments are allowed. | |
Python's function call syntax is name(arg1, arg2, arg3, ...). Positional, optional, keyword, and I _think_ "rest" arguments are all allowed. | |
CL has no statements or operators - every piece of code is an expression. | |
Python makes a distinction between expressions, statements, and operands. | |
CL calls modules "packages" and you reference them using a colon: package:name | |
Python modules are referenced using a period: package.name | |
CL is a "Lisp-2": the same symbol "foo" can name both a variable and a function, depending on context (and, well, a lot of other things: https://stackoverflow.com/questions/11876815/common-lisp-a-lisp-n): (foo foo) <- different "foo"s | |
Python is a "Lisp-1": if you declare a function "foo", then that just stores the function object in the variable "foo". foo(foo) calls foo on its own function object | |
CL syntax can be extended through the following: | |
normal macros: make (foo bar) be rewritten to whatever expression you want | |
symbol-macros: make bar, just the symbol by itself, be rewritten to whatever you want (so you could expand it to an object reference) | |
read-macros: violate the normal lisp rules and read in JSON natively: https://lisper.in/reader-macros [true, false, null] => #(T NIL NIL) | |
setf expanders: value assignment in CL (x = 10) is generally done with the setf macro: (setf x 10). setf expanders allow you to write code that looks like it's changing the return value of a function, but really expands into code that changes some value that the function is accessing | |
for instance, if you have a "get-first-even-element" function that operates on a list | |
(get-first-even-element (list 1 3 4 5)) => 4 | |
you could define a setf macro that changes that value | |
(setf (get-first-even-element (list 1 3 4 5)) "foo") => 4 | |
but now the original list has "foo" in place of 4 | |
generic functions: this is kind of a cheat, though, because the function call syntax is the same as the object reference syntax | |
Python syntax can be extended through decorators, which wrap the decorated function in the code of the decorator | |
CL names variables with symbols. this allows you to use any character in a variable name as long as it's escaped like |foo bar|. the list of *escaped* characters is much smaller, but includes -_!?%&.*+/$=<> and numbers as long as they don't lead the rest of the name | |
Python allows you to name variables with (non-leading) numbers, upper- and lower-case letters, and the underscore | |
CL names use-kebab-case-to-separate-words | |
Python names use_snake_case_to_separate_words | |
CL names are "case-insensitive" (by default - things are named with symbols, which are interned from the reader, which makes everything uppercase by default - but this is configurable) | |
Python names are case-sensitive | |
Values: | |
CL has a native symbolic datatype that is interned from strings. Symbols are used to name everything, usually are implemented as structures with references to the code of the named function/macro/special form, variable bindings, types, etc. as well as a "property list" slot that allows you to store arbitrary data in them | |
Python does not have a native symbolic datatype, BUT constant strings are interned, so comparing raw strings is as efficient as symbols (just a pointer comparison, because with interned strings, two strings with the same contents are guaranteed to be represented with references to the same interned object) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment