Neither Ruby nor Python support the idea of 'code is data'. Sure they are simpler. The concept of code is data is not about simplicity, it's there to support code translations.
Most languages have constructs which don't behave like function calls and which have special evaluation rules. A simple example is the IF statement. It takes a condition and two expressions. Depending on the condition only one of the expressions will be evaluated. If you look around, you'll find a bunch of such constructs in most languages. Lisp also has such an IF operator. Lisp additionally allows us to write any amount of additional operators which have evaluation rules, which are not the evalation rule of a function call.
Is the IF statement irregular? It isn't. It's just one of many operators which does not follow the evaluation rule of a function call.
Something like Ruby has a lot of weirdness in its calling mechanism. Python has strange non-local transfers going on. There are lots of ways we can perceive 'irregularities'.
> Neither Ruby nor Python support the idea of 'code is data'.
Well, Python and Ruby have an "eval" statement. So "code as data" is supported, but discouraged.
In contrast Lisp macros / "code as data" are commonly used.
Why is there a difference in culture?
Is the only reason, that strings are a lot harder to manipulate than lists?
Is there more to it?
Maybe translated code is harder to maintain and reason about?
Maybe debugging and profiling is a lot harder (this is true for C macros at least).
I have just shared one confusion that I was struggling with.
I found python and ruby much easier to read and pick up.
One reason for this is, that programmers are not allowed to define new irregularities.
Maybe this insight is helpful for others, who are getting Lisp as well.
At least this was my motivation for writing this post.
> I found python and ruby much easier to read and pick up. One reason for this is, that programmers are not allowed to define new irregularities.
The mechanisms they give you are more than enough, though. Ruby especially
favours this kind of thing a lot; how many people really have any idea what's
going on (and how many of them beyond "something to do with method_missing and
instance_eval") when they write something like:
FactoryBot.define do
factory :foo do
bar { 'baz' }
end
end
I don't think understanding that it's all blocks and method calls wins you
that much.
Python and Ruby use flat strings as input to eval. That's an entirely different thing. To do ANY useful code transformations one needs to parse it. Lisp code already comes in a hierarchical token tree.
Ruby and Python represent code as text. That's what Lisp does, too. One has a file of code. But Lisp has something which neither Python and Ruby do: the text is actually a data structure: lists, strings, numbers, vectors, symbols. Lisp provides a function READ.
Or use infix conversion during reading. Write your code with sections of a more conventional syntax:
CL-USER 80 > (defun hypot (a b)
"Compute the length of the hypotenuse of a right triangle
with sides A and B."
#I( sqrt(a^^2 + b^^2) ))
HYPOT
CL-USER 81 > (hypot 1 2)
2.236068
Just put a quote in front of the expression and enter it at the repl -> the result is the transformed code.
CL-USER 84 > '#I( sqrt(a^^2 + b^^2) )
(SQRT (+ (EXPT A 2) (EXPT B 2)))
There is nothing like this in Ruby or Python.
If you look at a Lisp interpreter, it actually executes Lisp data as code. Macros transform Lisp data as code. This is far far away from what Ruby or Python do. Python provides some access to its AST - that's also far away from what Lisp does.
Lisp macros don't define irregularities. They do code transformations. Stuff where the language designer/implementor of, say, Python refuses to provide a control structure, you can do it yourself in Lisp in a few minutes or you can spend years to implement really cool stuff. The Common Lisp object system started as a large portable library (with only very little system dependent code), which code be loaded into Lisp and which the provided a very elaborate object system. This also included some clever macro code.
I think titles like 'The problem of Lisp' may need a bit more reflection and patience. What is new for you is old news for the Lisp community. The exact same problem report can be already read in Lisp books from the 1960s. It's a bit like blogging 'The world isn't flat!.;-)
> I think titles like 'The problem of Lisp' may need a bit more reflection and patience.
Forgive me for the baity title ;) I realise this is not a new discovery.
> Python and Ruby use flat strings as input to eval. That's an entirely different thing.
I understand this. I was responding to the earlier assertion "Neither Ruby nor Python support the idea of 'code is data'." which is not entirely correct. You can clearly supply code as string data. (Python's namedtuple does make use of this). It's just not nearly as powerful.
Thanks for providing the examples. The infix macro is pretty amazing! I will make use of this #I stuff.
> Neither Ruby nor Python support the idea of 'code is data'." which is not entirely correct. You can clearly supply code as string data
That's just some kind of eval feature. Many languages can execute strings of code at runtime - for example one could write C code to a tmp file, call the compiler, and load the object file -> that's how some Lisp-to-C compilers work. But it's just the usual C code as text.
That's not 'code is data'. Code is data means that code is actually written in a data structure which is serialized and de-serialized. In Lisp code is written as s-expressions.
'code is data' does not mean I can treat code as a string and evaluate it, it means: code is actually read, written and manipulated as structured data - and not as blob of text.
Ruby code is written as textual Ruby code, where Lisp is written as Lisp programs in s-expressions.
What you tell me is the code is text, which can be somehow executed at runtime, which is an entirely different thing.