As I understand, earlier forms of life used RNAs as building blocks (instead of proteins encoded in DNA->RNA), so protein-based life _was_ a completely different form of life.
Some of the oldest replication machinery in our cells still uses the good old rusty RNA building blocks at its core (however nowadays they're propped up with proteins), and the newer machinery is almost entirely "high tech" proteins.
So you could say that in the billions of years, entirely new life forms were created, and they just completely displaced the older, less effecient ones. Probably pure-RNA life forms were not even the first ones, and they completely displaced even more primitive prior biotechnology when they appeared.
I feel like people who program in JavaScript or whose projects pull megabytes of dependencies, don’t get a moral right to complain about this. You guys just sit and calm down this time, you already said what you could.
Your app takes 20 seconds to load, pulling 50 megabytes of minified JS. Your backend is a mess of 20 Rust microservices, 300 megabytes docker image each.
Nobody has actually been reading and understanding code in your org for the past 15 years. And nobody has ever been responsible, everybody has just been job hopping for a 15% total comp bump.
"letting me" is hardly how I'd put it. More like I would refuse to eat any and all food unless committed to a hospital stay. Eating Cheerios and taking supplements was a compromise for my exhausted parents and they were more at a loss of what else to do. I don't think you understand what living with someone with moderate to severe autism is like. It's not being a "willful child" it's an entirely different experience than what most parents are exposed to.
> You end up rebuilding semantics over and over again (validation, mapping, enrichment), and a lot of failures only show up at runtime.
I think die-hard fans of static typing mostly fail to acknowledge this objective reality and its implications. Every time they encounter this problem again and again, they approach it as if nobody thought of this before, and didn’t develop reliable abstractions to productively work in these environments.
The way I see it, Clojure was created to address most of the philosophical problems highlighted in the article, and its ecosystem has developed under the influence of such ideas.
It embraces the dynamic nature of things. Every program that must do I/O is dynamically typed at the edges - so statically-typed languages must have both the facilities for dealing with dynamic types and static types, and use those differently for "inner" and "outer" parts of the program. The Clojure ecosystem makes it more approachable to work with dynamic data, and as a result, program's parts (modules, even functions) use the same machinery as the program as a whole. What helps, off the top of my head:
1. Immutability of values, persistent data structures, explicit state management - it turns out most of the "problems" with dynamic types are not due to the fact that types are unknown, but the fact that they may change over time. Most of concurrency problems also become irrelevant with immutable values.
2. Powerful data specs/schemas allowing to express full business requirements and constraints on expressible values (not possible with static typing anyway) — making invalid states unrepresentable and actually meaning it.
3. Concise unified model for working with different logical data structures, also nested data structures.
4. Simple asynchronous communication mechanisms and patterns between independent modules of a programs.
5. Rich extensible aggregates of data with namespaced keys as the object model.
6. An ethos of extreme backward compatibility (and tools for achieving and maintaining it) through accretion of novel values/parameters/features, enabling reloadable workflows, which makes it easy not only to redefine functions in code while program is running, but also updating individual programs while interconnected system is running, without breaking things.
The downside is that tools that reduce complexity are not very easy to use for people with less experience, so they cannot be too popular. People tend to overcomplicate, and to be quick to dismiss well-thought-out solutions.
When round brackets are used, the first element in the list defines how the rest of the list is interpreted, for example:
(func a b c) — run a function with its parameters
(macro x y z) — expand a macro with its parameters
([p q r] …) — “bare” function body that starts with a vector of parameters, and executable forms follow.
Square brackets are used where elements are the same “kind”, and the first one is not special, e.g.:
(defn f [a b c] …) — a collection of same-kind parameters, the first parameter is not special
(let [a 1 b 2] …) — a collection of bindings, the first binding is not special
The only exception that comes to mind is grouping multiple matching elements in `case`, but it for ergonomics.
Once I got the logic, when which is used, I changed my mind, and ever since I’ve felt it’s beautiful.
reply