How does lisp compare to Ruby/Elixir?

You can view quoting something as simply “return this as a symbol”. (square 10) is the function application of square with the argument 10 and '(square 10) is actually a list with the symbol 'square and 10 as members, meaning (list 'square 10). Having a ' before a list is a shorthand for saying “This is a list value and everything inside this list is an atom, meaning a literal value”. Any symbols inside that list will be automatically quoted. Hence: (= '(square 10) (list 'square 10)).

You can view the execution of these forms as having the following conditions: If the compiler sees an unquoted symbol it will get the value of that binding. If that binding is the first element of a list (but not a literal list, simply source code) it will try to apply that function to the remainder of the elements of that list.

By convention there are forms where Racketeers use square brackets instead of parenthesis in order to differentiate different parts of the forms. Binding forms where we bind several things are one of these. You can (but shouldn’t) use regular parens in these and it’ll work exactly the same. It’s generally thought of as increasing readability.

Since p12 binds to one thing and p21 binds to another in this let form, the square brackets are used to differentiate them.

3 Likes

OOooooooo… so it’s not the entire thing thats a “symbol” (so not like a string at all), its actually just a list of things that have been “symbol-ized”

So for this, how is a symbol and a string different? (list 'square "square")

And this maybe a stupid question, the '(these will be symbols "but not this") won’t turn a string into a symbol right? It’s hard to tell from the output

Thanks for answering all these questions. I’m about to finish this tutorial and move on to this more in depth guide https://docs.racket-lang.org/guide/to-scheme.html

I know it’s not much… at all… but here is some Racket code I wrote completely on my own (I looked at the tutorial and only looked at how they call the function, and hid the part about defining it. Then I’d define it myself and see if it matched). Feels SUPER good to get this far, even though I know this is probably level 1 of 10. It’s definitely mindwarp.

#lang slideshow

(define (square n)
  (filled-rectangle n n))

(define (series mk)
  (hc-append 4 (mk 5) (mk 10) (mk 20)))

(define (rgb-series mk)
  (vc-append
   (series (lambda (sz) (colorize (mk sz) "red")))
   (series (lambda (sz) (colorize (mk sz) "green")))
   (colorize (series mk) "blue")))

(define (rgb-maker mk)
  (lambda (sz)
    (vc-append
     (colorize (mk sz) "red")
     (colorize (mk sz) "green")
     (colorize (mk sz) "blue"))))

(series (rgb-maker square))

; C-f  Move forward
; C-b  Move backward
; C-p  Move up
; C-n  Move down
; C-a  Beginning of line
; C-e  End of line
; C-d  Delete forward
; C-h  Delete backwards
1 Like

One difference would be that the symbol is actually an atomic value, it can’t be split into parts at all. The string has parts and can be used as such. You can go from string to symbol and back with the functions string->symbol and symbol->string ((symbol->string 'elixir) ; "elixir").

No, the identifiers in the list will be symbols and numbers, strings, etc. will be whichever datatype they were before. You’ll learn more about this when you learn about the datatypes of Racket.

The quoted list form actually produces the list with every item in the list having quote called on it:

> (quote elixir)
'elixir
> (quote "elixir")
"elixir"
> (quote 42)
42
> (quote 3.14)
3.14
> (quote (function-call argument-one argument-two))
'(function-call argument-one argument-two)

As you can see, if you tried to quote a function call you’d get the actual form instead of the return value of the function. If you stumble upon that scenario, be sure to call list when you want to create a list instead. Later on you can look at something called “quasiquoting” which will allow you to use short form for creating lists but still embedding values from identifiers and function calls. It makes for pretty confusing notation for a beginner, though, so there’s no shame in leaving it for later.

It’s a beautiful thing to see someone excited and don’t sell your inspiration and discovery short. Discovering and figuring a programming language out is a wonderful thing and you should enjoy it. Well done! I hope you find as much joy in Racket as I have.

Regarding this being “level 1 of 10”, you would be surprised by how simple the world of Racket can be. Creating GUI applications in Racket isn’t that much different from what you did in the tutorial and all of Racket fundamentally is about the same simple concepts applied, sometimes in a nested fashion. If you just retain the willingness to explore you’ll be able to do wonderful things in no time.

6 Likes

Awesome, thanks for that explanation. ALl what you said makes sense. I’ll just keep reading :smiley:

1 Like

Yes, I didn’t explicitly mention that the do-what doesn’t have to be a function as such. It can also be a special form which is interpreted in a special way or a macro which will be expanded. A simple example of a special form is (if test (do-if-true) (do-if-false)) where the “arguments” aren’t all evaluated before “calling” if but in the standard way, first (test) is evaluated and then either (do-if-true) or (do-if-false) depending on the test.

The benefit, I think, of lisp is that the syntax is always the same, it’s always the (do-what arg-1 arg-2 ...) everywhere. Consistency, consistency and even more consistency.

And yes I very well know that if might be implemented as a macro but the evaluation stays the same.

1 Like

I recently started to read “Structure and Implementation of Computer Programs” by H. Abelson and G.J. Sussmann, an introduction into both computer science and Scheme, which is used in examples and exercises.

What touched me immediately was the composibilty of Lisp. In Elixir, we like the view of data piped through our functions. We frequently use the Enum module, and many operations boil down to map, filter and reduce. We could do quite the same in Ruby with using map, select and reduce, and you can go the same path in many other languages. For example in C# using LINQ, which is in fact a select, where and aggregate on enumerables.

Same thing with different names, making data transformations and programs far more readable and composable. So, in a legacy C# app I started about 12 years ago, any time I stumble about a for loop, I think about refactoring it to use building blocks like filter, map and reduce.

And now, I’m reading a book published 20 years ago using some “esoteric” language, and it just took a hundred pages to show how map, filter and reduce are the building blocks for any data transformation in lisp. Just using the simple language features of Scheme: list of cons cells, recursion and defining functions to abstract the list processing into your building blocks. Without any syntatic fuss about it.

I should have read the book when started CS, and had been a better programmer since then. But then again, sometimes you need some experience to appreciate the wisdom in front of you.

Imho, Lisp is just the paramount of composability - the beauty of S-expressions put to the extreme - sometimes maybe at the expense of readability and performance.

5 Likes