HN Theater @HNTheaterMonth

The best talks and videos of Hacker News.

Hacker News Comments on
Clojure Made Simple

Oracle Developers · Youtube · 29 HN points · 9 HN comments
HN Theater has aggregated all Hacker News stories and comments that mention Oracle Developers's video "Clojure Made Simple".
Youtube Summary
In the seven years following its initial release, Clojure has become a popular alternative language on the JVM, seeing production use at financial firms, major retailers, analytics companies, and startups large and small. It has done so while remaining decidedly alternative—eschewing object orientation for functional programming, C-derived syntax for code-as-data, static typing for dynamic typing, REPL-driven development, and so on. Underpinning these differences is a commitment to the principle that we should be building our systems out of fundamentally simpler materials. This session looks at what makes Clojure different and why.


Author:
Rich Hickey
Rich Hickey, the author of Clojure and designer of Datomic, is a software developer with over 25 years of experience in various domains. Rich has worked on scheduling systems, broadcast automation, audio analysis and fingerprinting, database design, yield management, exit poll systems, and machine listening, in a variety of languages.
View more trainings by Rich Hickey at https://www.parleys.com/author/rich-hickey-1

Find more related tutorials at https://www.parleys.com/category/developer-training-tutorials
HN Theater Rankings

Hacker News Stories and Comments

All the comments and stories posted to Hacker News that reference this video.
I still think this is a great appeal of Clojure(Script), you often work with the "flat" "just data" representations directly. From https://youtu.be/VSdnJDO-xdg?t=3000 (transcript: https://github.com/matthiasn/talk-transcripts/blob/master/Hi...):

    Example - HttpServletRequest
    
    getAsyncContext, getAttribute, getAttributeNames,
    getCharacterEncoding, getContentLength,
    getContentType, getDispatcherType, getInputStream,
    getLocalAddr, getLocale, getLocales, getLocalName,
    getLocalPort, getParameter, getParameterMap,
    getParameterNames, getParameterValues, getProtocol,
    getReader, getRealPath, getRemoteAddr, getRemoteHost,
    getRemotePort, getRequestDispatcher, getScheme,
    getServerName, getServerPort, getServletContext,
    isAsyncStarted, isAsyncSupported, isSecure,
    removeAttribute, setAttribute, setCharacterEncoding,
    startAsync, startAsync...
    getHeader, getHeaderNames, getHeaders, getIntHeader...
"How many maps?" Vs.

    In Clojure, Just Use Maps
    
    {:remote-addr "127.0.0.1",
     :scheme :http,
     :query-params {"somekey" "somevalue"},
     :form-params {},
     :request-method :get,
     :query-string "somekey=somevalue",
     :content-type nil,
     :uri "/foobaz",
     :server-name "localhost",
     :params {"somekey" "somevalue"},
     :headers
            {"accept-encoding" "gzip, deflate",
              "connection" "close",
              "user-agent" "Apache-HttpClient/4.1.2 (java 1.5)",
              "content-length" "0",
              "host" "localhost:8383"},
     :content-length 0,
     :server-port 8383,
     :character-encoding nil}
"In Clojure, we just use maps. This stuff came over a wire in HTTP, as text. How did we turn it into this [the Java]?"
Sinidir
This will never cease to produce laughter in me :)
eyelidlessness
This is almost exactly TypeScript’s worldview at the type level. It’s just structural types. The only difference is that it’s statically analyzable as a core feature.

JS is a little more complicated than that, but (sorry to tell you) so is ClojureScript, where a “map” can become a bunch of different actual data types through the course of a normal runtime.

In all cases, sending plain maps/pojos is nice but receiving them is fraught. Again TypeScript helps here by providing type guards where your parse/validation code can be statically analyzed.

Edit: and in terms of serialization, all of these languages struggle with fidelity loss. Clojure has EDN or Transit if you’re willing to incur one cost or another. But by default you’re sending out a smaller subset of types than your runtime is using, and receiving the same.

fulafel
I'm guessing you mean it's similar to the map case?

Clojure/Script of course approaches validation and parsing using schema systems (malli or spec) that has its advantages compared to static type based approaches.

eyelidlessness
I mean TypeScript’s structural type system is effectively “just use maps”. With very few exceptions, any structurally equivalent type is substitutable for another.

> Clojure/Script of course approaches validation and parsing using schema systems (malli or spec) that has its advantages compared to static type based approaches.

So does TypeScript. It has to, because the type system doesn’t have a runtime. The benefit in TS is that you also get static analysis.

fulafel
Can you point to some exemplar TS schema system(s) suitable for structured data parsing and validation? I've seen io-ts but it seems quite clunky in comparison, but maybe it's not representative.
fulafel
Replying to myself - Zod seems a better TS side library for this.
eyelidlessness
Yes, I would have pointed to Zod. There are a lot of options in the space, but I think there’s still room for improvement.

My (vaporware, but I’ve built something similar before on top of io-ts) project would provide a similar API, but with base types built from JSON Schema definitions, and automatically generate docs.

There are also opportunities to significantly improve runtime performance (at the expense of build time) by avoiding unnecessary parsing where statically checked code paths are already valid, eg naive multiple parse steps.

Waterluvian
Typescript is fundamentally bad for this because the type system does not exist at runtime. I’ve yet to find one I like so I just hand roll both typings and JSON schema. Particularly because I want the schema for other languages too.
Oct 30, 2020 · randomsearch on If Not SPAs, What?
> Out of curiosity, as a Java enthusiast, I was wondering if you could give examples of what you feel is wrong?

I think there's a lot of criticism for Java's language design. It involves an awful lot of boiler plate, and is generally very verbose. It's also a language that forces you to use OO, and OO has received a lot of pushback over recent years - so that approach has become very unpopular.

Personally, I also think the use of so many design patterns is an attempt to compensate for what the language lacks, its reflection capabilities are flawed etc.

I don't hate on Java. For many years it was my main language. It has awesome tooling and the JVM is incredible. But I do agree with most of the criticism.

I've been learning Clojure recently and Rich Hickey's talks often begin with some motivation including criticism of Java and OO more generally, here's one such video: https://www.youtube.com/watch?v=VSdnJDO-xdg

This is alluded to in Price #2: No Packaging. I’d argue that grouping functions into namespaces largely resolves this problem.

These two slides in the Clojure, Made Simple talk are my favorite counterpoint to even shallow objects being easier to work with than data. See starting at 50:00:

https://youtu.be/VSdnJDO-xdg

If you don’t want to click through, the list of HttpServletRequest methods is bigger than the map itself! Each one is its own little DSL, and they are not consistent with each other. To do anything with the data, you first have to figure out which method gets the bit you want out of the thing. And that’s a simple case—the complexity only goes up from there.

The map OTOH is just a map; you have hundreds of simple, composable functions that you can use to slice and dice the data however you need.

Want the keys? (keys request)

Want the keys of the headers? (keys (:headers request))

The user-agent value? (get-in request [:headers “user-agent”])

None of these are specific to a servlet request; they will work on any piece of data. And it’s trivial and transparent to build more specific functions out of them that fit your problem domain.

I agree with the grandparent comment. The parent comment sounds like a random and non representative sampling of what makes Clojure unique.

Both parent and grandparent correctly identify that many Clojure ideas come from elsewhere. Lisp and hosting on the JVM were not new. Immutability and persistent data structures weren't new ideas.

But putting them into one and making it performant was a paper published by people in the Clojure community (Rich was the lead author).

Clojure describes itself as data-oriented. That means not just the primacy of data, but of plain data, unadorned: https://youtu.be/VSdnJDO-xdg

What I find almost unique Clojure is that there's a philosophy to it that resonates throughout in it's design decisions: simplicity. Data is simpler than code, and code is simpler than macros, so prefer data and leave macros so a last resort. Libraries are simple, frameworks are complex. Plain data is simple, and the knock on effects of every library converting to & from plain data is powerful. But it only reveals itself after using Clojure for a while. Immutability allows for simpler code that you can reason about. Isolating state into containers allows you to characterize, control, and reduce touch points with state, which is necessary yet a source of complexity.

You can see the same set of ideas that underpin the design of Clojure to be present in follow on library additions (reducers/transducers, async, Datomic, etc). There's simplicity, often manifested in immutability and the associative model of information (everything is a map), at least.

dwohnitmok
> everything is a map

This I strongly agree with. Clojure's insistence on carrying through with its core immutable collections everywhere it can is a defining feature of the language IMO.

I mean at the end of the day we're talking about personal influence so I can't really argue with you thinking that what I'm talking about is random.

Nonetheless personally I don't find the JVM to be a definitional feature of Clojure. E.g. Clojurescript feels just as Clojure-y to me as JVM Clojure.

Likewise I think simplicity is in the eyes of the beholder.

Personally I think (the current presentation of) transducers and core.async are both too complex (and in the former's case complected to use a Clojure-ism).

The latter I think is better served by manifold and the former, man I really need to write this up at some point since this keeps coming up, but transducers don't need to be higher order functions on reducers. Every transducer is exactly equivalent to a function from a -> List b (again yes this presentation is agnostic of source and therefore holds even for something like channels despite the presence of a concrete list). The only thing that you get from its presentation as a higher order function is reusing function composition which I view as bearing all the hallmarks of complection. It goes the "opposite" direction you'd expect and it's hardly ever used as "function" composition (when's the last time you composed a transducer with something that wasn't another transducer?).

May 28, 2019 · 8 points, 0 comments · submitted by tosh
Apr 27, 2017 · 2 points, 0 comments · submitted by tosh
Not sure if unthinkable is the right word:

Simple Made Easy: https://www.infoq.com/presentations/Simple-Made-Easy

David Nolen talks about how immutable structures work: https://youtu.be/SiFwRtCnxv4?t=504

Objects are Marionettes: https://www.youtube.com/watch?v=VSdnJDO-xdg&feature=youtu.be...

grzm
Not sure if unthinkable is the right word

That's exactly what I'm asking about. I'm familiar with Hickey and Nolen.

Nov 21, 2016 · 1 points, 0 comments · submitted by tosh
You don't need an IDE for Clojure, IMO, huge IDEs like intellij/cursive are anti-clojure, Clojure culture encourages to write programs composed of small, modular components that can be tested individually, so mostly you keep things in your head, and if you haven't work with it in a while, is easy pick up again, Clojure doesn't need a "dot operator" to autocomplete to million of different API classes/methods. Rich Hickey makes a comparison here: https://youtu.be/VSdnJDO-xdg?t=2943

The thing is that most Clojure users are/were heavy Java users and they feel awkward without their IDEs and also some have a big java project that hey mix with clojure.

I've been using ClojureScript in production for about a year now. The runtime is very fast, and in some cases it actually outperforms native Js. For example, both Om and Reagent are actually faster than React.js itself (http://www.youtube.com/watch?v=VSdnJDO-xdg&t=9m36s).

Debugging story is very good. You pretty much never have to see the generated JavaScript. ClojureScript uses source maps, so any errors in the compiled Js will map back to the original ClojureScript source that caused them. Chrome dev tools even do syntax highlighting for ClojureScript now.

Having a live dev environment with Figwheel and a REPL goes a long way here as well. You can inspect a lot of things at runtime very easily.

Using immutable data structures by default avoids a lot of the debugging pain as well. You practically never have to trace through a bunch of function calls to find the problem. In most cases the problem is exactly on the line where the error occurred.

draw_down
What if your thing crashes in IE?
yogthos
IE 11 supports source maps, so that's helpful. The other nice thing is that you rarely end up dealing with browser specific quirks since all popular ClojureScript libraries piggie back on React.js and the Google Closure library.

These do a great job of handling the platform specific quirks for you. The first app I developed with Reagent actually had to run on IE8, and to my shock and surprised worked without any issues after I added the standard IE shims.

So, overall my experience is that you're better off dealing with IE than with most Js frameworks.

"Runtime", or "REPL" time, or "doc time" are all ways you can just "look at it". Do you think knowing "x" is type "Foo" tells you anything about what you can do with "x", other than use it where you can use "Foo"s? You need to figure out what you can do with "Foo" by reading the docs around "Foo". Yeah, I know a priori I can call a method on Foo that returns an int and pass that to other int-taking methods, but is that what I want to do? Which method returning an int do I want to call? You need to look at what "Foo" is to determine this. Rich's example is HttpRequestServlet in https://www.youtube.com/watch?v=VSdnJDO-xdg I'd recommend watching that and ignoring me. A lot of the issues he raises, as contextfree points out, are around OOP rather than static typing in general so take these as my views and not necessarily his...

If "Foo" is just dynamic data, perhaps in a map, composed of other data, and it's values all the way down, any of those three points of time are easy ways to look at "Foo" and can generally be faster than reading a JavaDoc page or ctrl+spacing figuring out what you have access to and what it all means and what you want to do with the data and code associated with "Foo". Since you're going to be looking at the thing you want anyway, I find it very uncommon in practice that I accidentally divide a string by 2. Static typing would let me know before I run the thing that I was an idiot (maybe, see next paragraph), but I'll find out almost as quickly when the code I just worked on (either in the REPL, which is very nice, but even from the starting state) actually does what I want -- you do that regardless of static/dynamic typing, right? You don't just write code and never verify it does what you want, trusting that it compiled is good enough? Anyway when I run it, I'll immediately hit a runtime error (which can be handled in Lisp in ways way more flexible than exceptions in other languages) and realize my stupidity. So I don't find that particular situation or class of bugs very compelling.

Nor do the presence of declared types alone guarantee my safety -- I need to know about any conversion rules, too, that may let a String be converted to an int for division purposes, or if there is operator overloading that could be in play. Conversion rules and operator overloading are interesting regardless of the static/dynamic typing situation because too much or too few can lead to annoyances and bugs. Java lets you concatenate ints into strings with "+", Python doesn't, but Python lets you duplicate strings with "*".

What I have run into in both static and dynamic languages is division-by-zero errors, which, unless you go all the way to something like Shen that supports dependent typing, your type system with "int" declarations will not help you with. I've run into errors (in both static and dynamic languages) parsing string contents to extract bits of data. I've run into errors around the timings of async operations. I've run into null pointer exceptions (though much less frequently in dynamic languages, maybe once the Option type is in wider use I can rely on that too but its issue is poisoning the AST). I've run into logic errors where I thought the system should be in state C but instead it transitioned to state E. The bugs that take the longest to diagnose and fix are simply due to not understanding the problem well enough and having an incorrect routine coded, so I have to figure out if it's a logic error or an off-by-one error or simply using the wrong function because it had a suggestive name and I didn't read through the doc all the way. Rich made an interesting point in one of his talks that all bugs have both passed your type checker (if you have one) and your test suite. My point is that I'm not convinced static typing as I'm given in the languages I work in is worth it for any reasons beyond performance because I don't see any other improvements like making me more productive or writing less bugs. I'm not opposed to types and schemas themselves in general, just static typing. I'd rather see pushes towards full dependent typing and systems like TLA+ (which has seen more industry usage than Haskell) and Coq than things like core.typed in Clojure.

The last point I'd raise is that I agree with you developer efficiency is more important than code efficiency. But this has been one of the mainstay arguments from the pro dynamic typing camp for many decades, with static typing advocates conceding that yes the developer will be more productive in a dynamic language but the performance is terrible for anything serious. If you want to argue static typing was actually more efficient all along from a developer perspective than dynamic typing, you have a lot of work to do. I'm not particularly interested in that argument (at least today) but the point still needs to be raised.

Aug 26, 2015 · huahaiy on The Beauty of Clojure
If you mean keywords by those used in maps, like {:some-key 1 :another-key 2}, yes, they have project wide semantics. However, these are just data. Changing the name of a key in data is just a matter of global search/replacement.

A fundamental point in the philosophy of Clojure is the emphasis on raw data. This is actually the reason why we love the language, because it lets us work directly with data, instead of dealing with language related complexity, such as API, type, syntax, etc. The Clojure community only realizes this advantage a couple of years ago. This Rich Hickey's talk in JavaOne gives a flavor of this idea: https://www.youtube.com/watch?v=VSdnJDO-xdg

Jul 04, 2015 · 10 points, 0 comments · submitted by McElroy
Jun 30, 2015 · 8 points, 0 comments · submitted by jonase
HN Theater is an independent project and is not operated by Y Combinator or any of the video hosting platforms linked to on this site.
~ yaj@
;laksdfhjdhksalkfj more things
yahnd.com ~ Privacy Policy ~
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.