A C macro with literals that lacks referential transparency:
#define MULTIPLY(x, y) x * y
int result = MULTIPLY(2 + 3, 4); // 14
Not knowing what something means does not make it bad, which is what I'm assuming you meant given how you phrased your sentence.Having a shared language of patterns and problems that occur in programming is a good thing. Ridiculing such terminology on the basis of "that group of programmers sure are weird" is pointless and counter productive.
Now if you relaxed just a little bit - the world would be much nicer place.
Because industry lied to you, promising "simplicity and riches". The industry didn't just overcomplicate programming. It institutionalized the complication. Why? Because complexity is a moat.
Complex frameworks need certified experts. Certified experts charge more. Companies built around expertise need the complexity to persist. So the complexity gets marketed as sophistication.
They've promised: "Java/C# will get you hired anywhere", but you're hired to write xml (these days yaml). "OOP models the real world", they said. The real world doesn't have abstract factory visitors. "Design patterns make you senior", but you only learned workarounds for language deficiencies. "Learn the framework, get the job". Framework dies, you start over. "Specialization is valuable". you're now hostage to one ecosystem.
A programmer who understands fundamentals is dangerous to this system. The fundamentals:
- a function transforms input to output.
- composition builds complexity from simplicity.
- types describe what's possible.
- effects should be explicit.
And then you realize that Lisp is the skeleton key. All that above is Lisp, or came from Lisp. Every language is either: Lisp with different syntax, or C with different syntax, or arguing between the two.
If you learn Lisp, you don't learn a language. You learn what languages are. You're no longer a consumer of a programming language or two, or a few. You are native speaker in all of them.
It’s kind of like in treesitter style editing, where you can “swap these two arguments,” “select this function,” “wrap this in a try block” with a single keyboard command… but way more standardized and granular. Plus with the ability to execute anything you highlight
All that and then you realize you can store code as data (since it’s just a data structure) and run data as code.
I think most programmers don’t realize how arbitrary the difference is between code and data until they get used to using LISP.
The concepts would be easier to grok up front if they just used normal function calls instead of "And now for this special syntax that only exists for this particular feature" which just adds more things to remember, instead of just the concepts themselves.
My point was, replacing n syntactic constructs by n functions or macros doesn’t reduce the cognitive load of having to know how each construct works. To the contrary, one can argue that everything having the same syntactic form makes it more difficult to distinguish different classes of features.
It was just an example, most languages have quirks like this. I don’t know about Java, but in Rust you have the turbofish operator, whose necessity stems from using the less-than sign as both an operator and a delimiter.
> My point was, replacing n syntactic constructs by n functions or macros doesn’t reduce the cognitive load of having to know each of them.
The difference is that if you don’t know a function/macro, you can just read its documentation. If you don’t know a syntactic construct, where do you look?
Another advantage is that if you want to create new functionality similar to existing language features, it won’t stick out like a sore thumb. For example, you could create an until loop:
(until (window-should-close)
(draw-screen))
# is equivalent to
(while (not (window-should-close))
(draw-screen))
In Rust, it would have to look completely different from a while loop: until!(window_should_close(),
draw_screen());
// is equivalent to
while window_should_close() {
draw_screen();
}I just made a library with [query syntax](https://codeberg.org/veqq/declarative-dsls) over various data structures a la sql:
(import declarative-dsls/dataframes :as df)
(def people (df/dataframe :name :age :job))
(df/dataframe? people)
(df/insert! {:name "Bob" :age 30 :job "Developer"} :into people)
(df/insert! {:name "Alice" :age 27 :job "Sales"} :into people)
(df/update! :set {:job "Engineer"}
:where |(= ($ :job) "Developer")
:from people)
(df/save-csv people "people.csv" :sep "\\t")
(def people2 (df/load-csv "people.csv" :sep "\\t"))
(-> people2
df/dataframe->rows
df/rows->dataframe
df/print-as-table)
Printing: job age name
-------- --- -----
Engineer 30 Bob
Sales 27 Alice
It also has datalog and minikanren (with s expr, sharing the same goals etc.) And it vectorizes like APL: (df/v + [1 2 3] 1 [1 2 3] 1) # returns: [4 6 8]
(df/v + 1 {:column [1 2 3] :key [1 2 3]}) # returns: {:column @[2 3 4] :key @[2 3 4]}
(df/v * [1 2 3] [[1 1 1]
[1 2 2]
[1 2 3]]) # returns: @[@[1 1 1] @[2 4 4] @[3 6 9]]
Or you can just use [J directly from Janet](https://git.sr.ht/~subsetpark/jnj): (jnj/j "3 4 $ i. 10") # returns: ((0 1 2 3) (4 5 6 7) (8 9 0 1))
(jnj/j "$" [3 4] (range 10)) # returns: ((0 1 2 3) (4 5 6 7) (8 9 0 1))
The Joy Web Framework has a cool [db query dsl](https://github.com/joy-framework/joy/blob/master/docs/databa...) too: `(var account (db/find-by :account :where {:login (auth-result :login)}))`, used for a [web auth](https://codeberg.org/veqq/janetdocs/src/commit/848dcbd8e54ad...).From my response, bigger than the article: https://lobste.rs/s/y0euno/why_janet_2023#c_lspe6n
I beg to differ. There's just isn't "easy and straightforward" path to simplicity. We thought that explaining the world with "objects" was simple and instead of using already existing language, OOP took "objects" (an easy choice) and invented a elaborate taxonomy of "patterns" to work around the limitations of objects. Just look at this mess:
- Strategy Pattern: Interface + multiple classes + dependency injection + factory maybe. Bruh, it's just a function that takes a function.
- Singleton: Private constructor + static instance + thread safety + double-checked locking. Bruh, it's a fucking value. You define it once. It doesn't change. You're done.
- Observer/Event System: Interface + listener registration + event loop + memory leak when you forget to unsubscribe. Bruh, tis a fucking function applied to a list (or stream).
- Decorator; Wrap a class in another class that implements the same interface. Bruh - it's function composition. You learned this in algebra class before you turned fourteen.
- Command: Encapsulate a method call as an object with execute(), undo(), history queue... It's a function stored in a variable. That's it. That's the pattern.
- Factory: Separate class whose entire job is to call constructors. Come on, it's just a fucking function.
- Template Method: Abstract base class with a method that calls abstract methods subclasses must override. It's a higher-order function.
- Iterator: Interface with hasNext() and next(), mutable state, ConcurrentModificationException. It's fucking map.
The Gang of Four book exists because Java made functions second-class citizens, so programmers spent 20 years building elaborate object scaffolding to simulate... functions. FP didn't solve these problems. It just never had them.
Yet somehow the industry likes to pretend that every programmer knows (or should know) OOP, while keep telling everyone how hard programming is.
Those who found the truth understand that there's a reason why Lisp just refuses to die and it's unlikely it ever will. At 70 years, it is still flourishing.
Frankly, though, I think lispy community has benefited from being smaller. For example, even though the now ancient Design Patterns already warned programmers to prefer composition over inheritance, the OO programmers still created 15 levels deep hierarchies.
Referential transparency is a funny name for a very powerful feature which helps you understand what the program does better, it's not a deeply abstract thing. Don't let the name scare you.
You could ask "why the funny name"? Well, specialized professionals use specialized jargon, even for "normal stuff". It's unreasonable to expect otherwise. Car mechanics also have weird names for car parts that are absolutely essential for the car and not that hard to understand if they explained them to you.