> Global atoms provide a good way to confine state into a single place.
I think it's simpler to pass state as an argument to your functions instead. That's less complex and easier to test.
> Last but not least, transducers make it onto my list of things to avoid... They are incredibly complex, even brought the spawn of the devil, volatile, into the language. Just for a bit of speedup and core.async integration.
Transducers are not just for performance and core.async. They're a hugely useful tool that allows the various collection functions to be applied to sources that are not easily seq-able.
For instance, say I have some source of events, and I can register a callback to receive events. With transducers I can still use all the standard collection functions, even though there isn't an obvious collection to use them on.
>> I think it's simpler to pass state as an argument to your functions instead. That's less complex and easier to test.
I was wondering what is the best course of action ini one particular situation. I am using your excellent libraries to create an HTTP API that needs to have state. I usually have an init function and using the ring init feature (see below). How would you not have an atom that hold lets say the DB connection info for a function that renders a html page displaying the version of the database, accessing the dbinfo with @dbinfo (that was updated at the init). Would it be possible to pass in to this function the state? Is it any different if I am calling @dbinfo in the actual function that renders the page or the calling function (router, or app definition)?
I don't think I've ever ran across a situation where I've needed STM. Most of the time you can just use pure functions, and when you absolutely need state, an atom is usually the best tool for the job. STM strikes me as a very specialised tool, at least in the context of Clojure.
I don't think metadata is a bad idea. It's data about data. As long as you don't assume something will have the same metadata as the data it's derived from, you should be fine. I don't necessarily like the way Reagent uses it, but it's not out of character for Clojure to use metadata semantically.
The article has some fairly reasonable rules of thumb to follow. For whatever reason I rarely find refs or agents to be useful, and lexical scoping is more limited and predictable than dynamic scoping.
That said, I feel it's a little too harsh in places. Metadata is fine so long as you don't expect it to persist to derived data structures. If you think of metadata as being about a particular piece of data and nothing else, then you won't run into problems. One place I find metadata particularly useful is annotating event data, which could allow, for example, treating events from different sources with different priorities.
I find that there are a lot of specialised tools in Clojure. Most of the time it's good advice to avoid them, but in the rare cases where they are needed, you're glad to have them. Keyword inheritance, for instance, is something I've been using recently, yet I've rarely had need of it before.
"You're definitely right, and from my experience the community has no interest in actually making the developer experience better."
Where did you get this impression? The State of Clojure 2015 survey indicates that the Clojure developers are very interested in making the developer experience better, particularly around error messages, startup time and documentation.
I cannot recall seeing a single Clojure developer suggest that Clojure's tooling is good enough. However, it is a lot better than it was.
I also use IVPN, and I'm extremely happy with them. I used to use Viscosity to access them, but recently I've switched over to IVPN's own software. It makes choosing multi-hops easier, and comes with a option to block non-VPN traffic.
I get the open-source concern. I prefer using stock OpenVPN. But if you're going to do that, you need to manage your DNS servers, and firewall leaks. I'm not aware that iVPN provides source code. You could ask them.
I tend to expect performance to be a problem with FP, but I don't tend find it to be the case in practice. Immutable data structures are slower than mutable data structures, but typically only by a fixed order of magnitude. FP also tends to make use of more functions and closures, but those are usually quite performant.
I'm a little curious as to why you find FP performance problematic. I guess you must be trying to solve some pretty CPU-intensive problems?
I guess it depends on what "influencing policy" means. Say a scientist using public funds happens to find results that contradict current government policy. Publishing the results could be considered as influencing policy, particularly if the opposition party picks it up and runs with it.
Isn't that what the 'Confusion reigns' in the title referring to?
"On 6 February, the government announced that any groups in receipt of public money will be banned from using those funds to attempt to influence either the government or Parliament."
"In theory, this could mean that scientists at UK universities are not allowed to tell ministers what the policy implications of their work are, or respond to consultations that touch on their area of interest — potentially removing their ability to comment on everything from climate change to medical regulation."
I'm not 100% sure on how the UK works, but in the US, if there is some new research that is done, and I'm invited to White House (or even to meet with a local senator, or give a talk as a conference with government personnel present), it's likely that the money to travel, speaker fees, etc are paid with grant money. This is generally 'normal'. What's the point of doing any research when I can't actually talk about the results with the people who are most likely going to be able to use it (since they paid for it)
I was specifically envisioning something like a government sponsored, or affiliated conference like speaking at an NOAA, DOD, etc workshop (which could fall under "attempt to influence ... government... parties"), which again is where the confusion comes from, and why the associated organizations are trying to bring clarity.
And for clarity, it's not a law, but new wording being put into all grant contracts. In it's entirety reads:
"The following costs are not Eligible Expenditure: Payments that support activity intended to influence or attempt to influence Parliament, government or political parties, or attempting to influence the awarding or renewal of contracts and grants, or attempting to influence legislative or regulatory action."
Real-world applications require backend systems with access control, mutable data, certain information being kept secret, and so on - something that seems fundamentally at odds with the design of IPFS.
I don't think that's at odds with the design.
If you want to keep something secret, you encrypt it. If you want to manage access control, you sign it. A client application can discard information that cannot be decrypted, or isn't signed by an authorised key.
You don't get atomicity guarantees of course, but you can often get away without them. I don't see a system like IPFS as a replacement for all of the web, just a sizeable percentage of it.
I think most are related to easiness, and so not recognized as an advantage by many Clojure acolytes (see: Rich Hickey's Simple Made Easy talk). Others are a consequence of shared state; also not recognized as a positive in Clojureland (rightfully so in most cases).
Here's my list:
- Database migrations
- Fast TDD (for some sub-Gary Bernhardt definition of fast) with access to the app environment.
- content negotiation
- Authentication with Devise and Omniauth
- Easy built-in support for caching
- Easy built-in support for background queue systems
- Easy support for one-off or occasional scripts with access to the whole app environment (rake tasks)
- Pry (yes, Clojure has awesome REPL powers, but Pry is so easy to use, and you have access to the whole app environment (no loading / switching namespaces or abuse of user.clj))
There's probably some solution to each of these. But all require you to find/know the options, and be able to evaluate which is best for you ("all REAL devs . . ." sure, sure). For Rails, all of these are available to you in the Rails Guides, except for Devise+Omniauth, which has an extensive wiki and tons of StackOverflow answers.
When I reflect on the ease of use question, I realize most of my frustrations with the Clojure REPL have actually been with trying to use it within an editor (Vim, Emacs, and Atom so far). And particularly with getting a ClojureScript REPL through those tools. That's not something I try to do with Pry, being content to use it from the terminal, so maybe not a totally fair comparison.
The app environment thing is mostly about having to switch between a lot of namespaces and/or to make sure they're all loaded upon starting the REPL. With Rails+Pry, you can work with any class in your app immediately. It also lets you put breakpoints in your code, where executing the code in a running process throws you into a REPL at that point, with all the local context up to that point of execution. Also, if you make code changes, you simply type "reload!" in the REPL and everything's up to date. I have a bookmark to read Stuart Sierra's post about his workflow to try to get a grip on how to come close to something like that in Clojure.
Have you tried using Cider under Emacs? Or Cursive? Both of those have debuggers that allow you to add a breakpoint in your code and then evaluate arbitrary expressions. They also allow you to step through each form and see the return value.
There are also a few projects for adding in debug REPLs, but those seem to have fallen by the wayside lately, perhaps supplanted by debuggers.
You might also want to talk a look at Duct (https://github.com/weavejester/duct), which is my ongoing and still early attempt at solving some of the framework problems with Clojure. If you create a template via Duct, like so:
lein new duct foobar +site +cljs
Then you some of the things you're asking for. The namespaces of your application are loaded when you start `lein repl`. You get a `(reset)` function that reloads your code, restarts your application and pushes those changes dynamically to the browser. You also get a ClojureScript REPL that hooks into the browser.
There are also generators, migrations, database support etc., but currently all inferior to Rails. I'm probably going to be working on the generators next, as they need attention.
'Lack' of frameworks. I always think it's kind of funny when starting a new Clojure web project, I have to explicitly insert a middleware for parsing parameter strings into a Clojure data structure, and then another middleware for transforming the string keys in that structure into keywords. There are just a bunch of things that don't magically work for you. That said, I really enjoy Clojure and appreciate/understand the high configurability.
TLDR; Yes, clojure is behind in regards of web development, but it is not as bad as you said. There is active work done in this area.
I totally agree with what you said. It is a general understanding in the clojure community that you compose libraries to get things done.
However, for web development that still means a lot of repitition, thats why I created the closp template.
My idea was to provide one way with sensible defaults to get started in doing web stuff in clojure.
I still work regularly on it and would be happy about contributions / feature requests. Whereas regularly means in terms of months as I do this all in my spare time.
Also I am quite active in the clojure community and I know that luminus is not shunned, but instead it also receives improvements and releases regularly. Also the author of luminus works professionally with clojure and with luminus.
And I have to admit, that luminus has the far superior documentation, he has done a good job on it I think.
These are precisely the kinds of things that Luminus http://www.luminusweb.net/ addresses. The template generates a project with all these things configured with reasonable defaults for a typical application. Luminus will also setup things like a logging configuration, authentication, databases, Swagger API, etc.
I think the fact that these things aren't magically is a big plus. You can use the defaults, but you can trivially customize the middleware stack for your specific needs.
Another thing to note is that a lot of the stuff that traditional frameworks do is geared towards doing presentation on the server. Modern apps tend to do presentation on the client, and the server primarily provides a set of stateless services for the client to fetch the data that it needs. With this approach, you don't really need a complex server framework in the first place.