← Back to context

Comment by pas

12 days ago

I tried Racked with the recommended IDE setup (VScode), does that have structural editing?

Here are my notes from 2023-07:

https://docs.racket-lang.org/more/index.html

    * okay, seriously. functional programming is nice, but these fucking parenthesis are ridiculous. the VSCode extension is okay, but doesn't help at all with formatting, etc.
    * "car" and "cons", yeey, but "first" would have been so hard?
    * the whole "define x 'x" is also meh.
    * no return, so sometimes something just takes the last one and returns.
    * there's string->url ... why not string->parse-url .. no, would have been too verbose. MAYBE YOU COULD HAVE SAVED SPACE BY OMITTING THE FUCKING PARENTHESES
    *

/ end notes

ehehe ... well ... I think I will keep trying it again every few years. is there a pythonish version, where indentation matters and no need for wrapping parens?

    * there's string->url ... why not string->parse-url .. no, would have been too verbose. MAYBE YOU COULD HAVE SAVED SPACE BY OMITTING THE FUCKING PARENTHESES

string->url is consistent with the way they do things in Racket. Note in that same document you linked the use of number->string and string->number, the -> indicates a type conversion. Along with string->url there is also the reverse, url->string, and some other conversion functions. That consistency is actually pretty nice, it means you can guess and check ("I have a string and want a url, will this work?" Oh, great it does!) or guess and search the docs before checking with the REPL or whatever.

https://docs.racket-lang.org/net/url.html

    * "car" and "cons", yeey, but "first" would have been so hard?

car shows up once, cons not at all, but he does use cdr. first, second, and rest are available, I don't know why he didn't use it in this demonstration. If you want to use first, go for it.

> these fucking parenthesis are ridiculous

They're just different. And once you've come familiar with the language, you miss s-expressions everyday, because they're just that easy to work with, especially with something like paredit. Why? because the grammar is easy to parse and reason about. The whole code is a tree. And evaluation is mostly working from the leaves to the root.

> "car" and "cons", yeey, but "first" would have been so hard?

It comes from the nature of the language. "cons" is to construct a pair of values, and "car" to get the first one, while "cdr" returns the second one. But lists are composed of cons cells (check how it works), and in that case you could argue for "head" and "tail" for the function names. But "car" and "cdr" were first and you could alias them easily.

> no return, so sometimes something just takes the last one and returns

The computing paradigm is expression evaluations, not sequence of instructions (although sequencing is there). An s-expression is always equivalent to something, and that something is what you're computing. Something like (first '("name" "email")) is the same as "name". Or (if (> x 0) :pos :neg) with x = 5 is the same as (if t :pos :neg) and the same as :pos. [0]

No return is needed. It's tricky when doing iteration, but whenever you're doing sequencing (they mention that in the docs for CL), the last value is equivalent to the whole thing.

[0]: https://en.wikipedia.org/wiki/Lambda_calculus#Reduction

  • other ASTs are trees too :)

    > It's tricky when doing iteration ...

    and that's my problem, that in the name of simplicity everything nice is thrown out. and "don't even think about it" and "you are holding it wrong" is the official motto. sure, I'm happy to adapt if I feel I got something in return, ie. memory safety with Rust, powerful type system in Scala, etc.

    all in all, sure, it's Turing-complete, and obviously millions of people already grok it and are productive in Lisps, but to me - and apparently to the vast majority of programmers - it's too foreign.

    • > I'm happy to adapt if I feel I got something in return

      Lisp is not a silver bullet. Whatever you can do with lisp, you can do with C or with JavaScript. What's different is how you do it. And it turns out that it's easier to create elegant solutions in Lisp as the mental model is heavily based on mathematics (lambda calculus). It's a different models of computing and solutions you're used to may no longer applied. Instead you reach out to a new way of solving the problem.

      When I say iteration is tricky, it's that most of the time, you relying on some mutable state to do the looping (i counter) and early termination, but in CL and Clojure, there often are easier ways.

      I'd recommend learning about computing models. Some solutions are easier to solve in one than the others. And now computers are powerful enough that we don't have to worry about performance (that often) and we can focus on creating better programs.

    • > memory safety with Rust, powerful type system in Scala

      ... easy and seamless user-defined compiler extensions with lisp

Your notes indeed suggest that you've not been using structural editing.

Aside from that, you could have tried to use `first` instead of `car`. It would've worked.

And yes, there happens to be a pythonic version named `Rhombus`, which is the flagship language of the racket system.

> but these fucking parenthesis are ridiculous

I thoroughly agree. I am deeply into functional programming, but syntax built entirely around endless nested parentheses has never felt like anything but a nightmare to me. Doubly so because even in 'clean' code it's reusing the same syntax with what are for most coders three clearly different logical concerns (defining functions, listing statements to execute in order, and invoking functions).

  • > what are for most coders three clearly different logical concerns

    That's the imperative model which foundations is the Turing machine. Lisp comes from lambda calculus and you're not doing the above really. At it's core there's the value (or the symbol that's represent it), then the abstraction, which defines how to get the value, and the application, which let you know with what to replace unknowns (variables) in your abstraction so you can get the value. And it's recursive.

    A program can be a value (not that useful), an abstraction (kinda like a nice theorem), or an application (the useful choice). Defining a function is creating a named abstraction, which is just a special value. Invoking a function is applying the parameters to that abstraction, which means replacing variables to get a more simplified version with the goal to finally have a value. If you can't get a value, that means the result is still an abstraction and then you still have to do more applications.

    You either have a symbol or atom (which is a value) or you have a list which is an application (except the empty list, which is the same thing as nil). An abstraction is created by special forms like (defun foo (bar) ...) in CL. but the result of the latter is still a symbol. An atom is equivalent to itself, and a list is equivalent to having applied the abstraction represented by the first element to the rest. Anything else is either special forms, macros, or syntactic sugar.

    So unless you're applying imperative thinking to the lambda calculus model, there's no confusion possible.

    • I don't have to be confused by it to dislike how it ends up working out in actual code, even "clean" code, in terms of ease of understanding and maintainability.