Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add RH answers page? #531

Open
puredanger opened this issue Jun 8, 2021 · 2 comments
Open

Add RH answers page? #531

puredanger opened this issue Jun 8, 2021 · 2 comments

Comments

@puredanger
Copy link
Member

Add a page similar to https://gist.github.com/reborg/dc8b0c96c397a56668905e2767fd697f to the guides. These are all quotes from Rich, so there is no problem here in migrating this content.

One thing I would like to do better is to contextualize these answers. In general, these are useful things to surface (we may need to do a bit of curation and review by Rich), but it's important for readers to understand that they are answers at a point in time. As such, they should clearly be quotes, and they should be visibly anchored to the time the answer was given, and whenever possible, a link or reference to the source, which often can provide further context. In some cases, there are useful follow-up links into the FAQ or other places on the site.

One open question is whether this should actually be integrated into https://clojure.org/guides/faq instead. My current thinking is no - while there is overlap, the framing and intent is different and they are both independently useful.

@puredanger
Copy link
Member Author

I have an initial start on this but it's not committed anywhere. I will track that down.

@puredanger
Copy link
Member Author

= Rich Answers
Rich Hickey
2019-06-30
:type: guides
:toc: macro
:icons: font

ifdef::env-github,env-browser[:outfilesuffix: .adoc]

toc::[]

This page contains quotes from Rich Hickey, the creator of Clojure, on a variety of questions and design decisions about Clojure.

## Language design

[[pattern_matching]]
**<<rich_answers#pattern_matching,Why no pattern matching?>>**

[quote, Rich Hickey, '2008, https://groups.google.com/d/msg/clojure/CqV6smX3R0o/3Xs_OEuHAQ0J[Clojure mailing list]']
____
If you are talking about the aspect of pattern matching that acts as a conditional based upon structure, I'm not a big fan. I feel about them the way I do about switch statements - they're brittle and inextensible. I find the extensive use of them in some functional languages an indication of the lack of polymorphism in those languages. Some simple uses are fine - i.e. empty list vs not, but Clojure's if handles that directly. More complex pattern matches end up being switches on type, with the following negatives:

* They are closed, enumerated in a single construct.
* They are frequently replicated.
* The binding aspect is used to rename structure components because the language throws the names away, presuming the types are enough. Again, redundantly, all over the app, with plenty of opportunity for error.

Polymorphic functions and maps are, IMO, preferable because:

* They are open, no single co-located enumeration of possibilities.
* There is no master switch, each "type" (really situation, with Clojure's multimethods) owner takes care of themselves.
* Maps allow for structures with named components where the names don't get lost.

I'd much rather use `(defrecord Color [r g b a])` and end up with maps with named parts than do color of _real*real*real*real_, and have to rename the parts in patterns matches everywhere (was it rgba or argb?)

I'm not saying pattern matching is wrong or has no utility, just that I would hope it would be less needed in idiomatic Clojure code. Certainly I'd want the binding part of pattern matching independent of the conditional part, and I would make any destructuring-bind for Clojure work with all of the data structures, not just lists, i.e. vectors and maps and metadata.
____

[[reader_macros]]
**<<rich_answers#reader_macros,Why does Clojure not allow reader macros like Common Lisp?>>**

[quote, Rich Hickey, '2008, https://groups.google.com/d/msg/clojure/uUiuQm5bDwU/6N2Po-OAypAJ[Clojure mailing list]']
____
People are going to put their Clojure programs in text files they expect to be read by the standard reader and evaluated, and it is a design goal of Clojure that programs by different people be able to be combined. User-defined reader macros don't compose, they clash.

You could make a version of Clojure with a modified reader - the reader source is there. But the programs you wrote to your version of the reader would be incompatible with those written by someone else who did the same thing. So, it has less to do with technical or licensing issues than it has to do with it being a bad idea, if you don't want to encourage programmers to create islands. CL has this feature and it is very rarely used for that exact reason - programs that require custom reader macros are a pain.

People have mentioned wanting to use the reader to read programs in other languages or data formats, and while I'm not generally opposed to the idea of a data reader whose output is not destined for the compiler, I'm also skeptical of the generality of such a thing. A lisp- style reader is a page or two of Clojure code. People are making parser combinator libs etc. There are ways to solve this problem without making the standard reader fungible, with the added complexity of providing an API and semantics for reader macros, and permanently leaving characters available for customization that that entails.
____

[[tail_call_optimization]]
**<<rich_answers#tail_call_optimization,Why no tail call optimization?>>**

[quote, Rich Hickey, '2010, https://groups.google.com/d/msg/clojure/4bSdsbperNE/tXdcmbiv4g0J[Clojure mailing list]']
____
When speaking about general TCO, we are not just talking about recursive self-calls, but also tail calls to other functions. Full TCO in the latter case is not possible on the JVM at present whilst preserving Java calling conventions (i.e without interpreting or inserting a trampoline etc).

While making self tail-calls into jumps would be easy (after all, that's what `recur` does), doing so implicitly would create the wrong expectations for those coming from, e.g. Scheme, which has full TCO. So, instead we have an explicit `recur` construct.

Essentially it boils down to the difference between a mere optimization and a semantic promise. Until I can make it a promise, I'd rather not have partial TCO.

Some people even prefer `recur` to the redundant restatement of the function name. In addition, recur can enforce tail-call position.

Full TCO optimizes all calls in the tail position, whether to the same function or another. No language that uses the JVM stack and calling convention (e.g. Clojure and Scala) can do TCO since it would require either stack manipulation capabilities not offered in the bytecode spec or direct support in the JVM. The latter is the best hope for functional languages on the JVM, but I'm not sure is a priority for Sun as tail-calls are not idiomatic for JRuby/Jython/ Groovy.

IMO, either a language has full TCO or it simply doesn't support using function calls for control flow, and shouldn't use the term. I didn't implement self-tail-call optimizations because I think, for people who rely on TCO, having some calls be optimized and some not is more a recipe for error than a feature.

So, Clojure has `recur`, which clearly labels the only situations that won't grow the stack, apologizes for no real TCO, doesn't pretend to support function calls for control flow, and anxiously awaits TCO in the JVM, at which point it will fully support it.

In addition to recur, there are lazy sequences and lazy-cons. To the extent you can structure your processing using the lazy sequence model, including making your own calls to lazy cons, you can write traditional-looking functional and recursive solutions that not only don't grow the stack, but also don't create fully-realized result lists on the heap, a terrific efficiency boost. I am not contending that this is as general as TCO, but it has other nice properties, like the ability to make results that are not tail calls (e.g. list builders) space efficient.
____

[[cons]]
**<<rich_answers#cons,Are there cons cells in Clojure?>>**

[quote, Rich Hickey, '2009, https://groups.google.com/d/msg/clojure/YY14rlMBuZk/5W43wvTEWqwJ[Clojure mailing list]']
____
There is a cons cell, it's called `Cons`, and `cons` produces one. It differs from CL cons cell in not being an arbitrary pair (i.e. the rest must be a sequence or nil).

Because Clojure supports a seq abstraction, there is heterogeneity in the implementations. In fact there are two cons-cell-like data structures:

[source,clojure]
-----
user=> (class (cons 4 '(1 2 3)))
clojure.lang.Cons
user=> (counted? (cons 4 '(1 2 3)))
false

user=> (class (conj '(1 2 3) 4))
clojure.lang.PersistentList
user=> (counted? (conj '(1 2 3) 4))
true
-----

So, `list?` is more specific because seqs are more general. `seq?` is probably what is desired. cons onto an `IPersistentList` could be made to return another `IPersistentList`, but currently doesn't.
____

## Language use

[[namespace_model]]
**<<rich_answers#namespace_model,Should I use a data structure for each instance and bundle functions that operate on that data structure into a namespace?>>**

[quote, Rich Hickey, '2008, https://groups.google.com/d/msg/clojure/Pk-3z8RYsx4/OJtKaEQrAnkJ[Clojure mailing list]']
____
There's a presumption built-in to that question that I think needs to be addressed first. That is, that there should be specialized, named, data structures/classes corresponding to imagined information-based entities in a system, and a partitioning of functions by their association with same entities, either in classes or modules. I've come to the conclusion after 20 years of C++/Java/C#/CLOS/OODBMS that this is a bad idea.

OO is not bad when there is an actual entity - i.e. a stream, a socket, a window, etc to which an object corresponds, or in a simulation of actual entities. That's where it was born and where it shines. When applied to the information/data in a system, however, it causes code to be 10x as large and 1/10th as useful.

For instance, don't customers buy products? Which should own the functions that involve both? Do databases have different functions for different tables? One would hope not. And if a DB query returns a recordset with fields from more than one table, should a custom entity be defined that maps to it? No - ORM is an even worse idea.

Data is data, and having very few general data structures to hold it and very many general functions to manipulate it is the key. That is the Clojure philosophy. (I'm not claiming that this is unique to Clojure, btw)

Look at the way clojure.xml processes XML - it deals with an external entity, a SAX parser fronting some XML source, gets the data out of it and into a map, and it's done. There aren't and won't be many more functions specific to XML. The world of functions for manipulating maps will now apply. And if it becomes necessary to have more XQuery-like capabilities in order to handle XML applications, they will be built for maps generally, and be available and reusable on all maps. Contrast this with your typical XML DOM, a huge collection of functions useful nowhere else.

Similarly, resultset-seq pulls the data out of a db query and into a sequence of maps right away, where it can be manipulated as generally as possible. That isn't to say you can't or shouldn't tag your data with some information/metadata helping you discern its source, use, or categorization in one or more contexts. But you should avoid the ghettoization of your logic, lest you have to repeat it endlessly.

That's a long way to say you might have to think a bit differently about your problem in Clojure in order to achieve the desired result of writing less, more powerful, code.
____

## Process

[[prs]]
**<<rich_answers#prs,Why do you use patches not pull requests?>>**

[quote, Rich Hickey, '2012, https://groups.google.com/d/msg/clojure/jWMaop_eVaQ/3M4gddaXDZoJ[Clojure mailing list]']
____
I prefer patches. I understand some people don't. Can't we just agree to disagree? Why must this be repeatedly gone over?

Here's how I see it. I've spent at least 100,000x as much time on Clojure as you will in the difference between producing a patch vs a pull request. The command is:

`git format-patch master --stdout > your-patch-file.diff`

There are two sides to change management - production/submission, and management/assessment/application/other-stewardship. People who argue that the process should be optimized for ease of submission are simply wrong. What if I said patches were twice as efficient for me to assess and manage as pull requests? (it's more than that) Do the math and figure out how the effort should best be distributed.

I don't think asking for patches is asking too much, and I truly appreciate the people who are going to the extra effort. And, I respect the fact that people disagree, and they might decide not to participate as a result. However, I don't need to justify my decision over and over. How about a little consideration for me, and the other list participants? There is a real diluting effect of get-it-off-my-chest messages such as these on the value of the list and the utility of reading it for myself and others.

Sometimes it's better to save it for your bartender :)
____

[[anchor]]
**<<rich_answers#anchor,Title?>>**

[quote, Rich Hickey, 'yy, https://groups.google.com/d/msg/clojure/Pk-3z8RYsx4/OJtKaEQrAnkJ[Clojure mailing list]']
____
____

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant