News for hackers. Stuff that matters.
Re:Clojure at Netflix (2013) [slides] by m12k on Wednesday October 31, 2018 @10:24AM (#18345921)
I'd like to learn a modern production-ready functional language (I have some academic experience with SML), since they seem like a good way to grow as a programmer. Main contenders so far are Haskell, Scala and Clojure (Reason might go on the list soon too) - but the fact that Clojure is dynamically typed is a bit of a turn off for me. My experience with dynamic vs static typing is that as a system grows in size and complexity, the value of static typing goes up. Doing a major refactor is one of those things that I find especially error-prone in dynamic languages, while statically typed languages make this much less daunting (of course they come with the opposite issue that you might not be able to compile for a day until you've fixed all the type errors - but I'd take this trade-off any day of the week). Can anyone with experience comment whether Clojure suffers from this too or if it somehow handles the lack of static typing better than other dynamic languages?
Re:Clojure at Netflix (2013) [slides] by jwr on Wednesday October 31, 2018 @02:05PM (#18347948)
Background: I have built and now maintain/extend a fairly complex application in Clojure/ClojureScript with 120k lines of Clojure code (for non-Clojure people: that is quite a bit, because of Clojure being so expressive).

I think the whole "lack of static typing" thing is a red herring, for two reasons:

1. Large-scale code is more about contracts that specifically "static typing". There are many contracts which you can't express using a type system.

2. Clojure doesn't "lack" anything. You can implement contracts for functions using pre/post conditions, use clojure.spec to implement arbitrary contracts over your data (and I do mean arbitrary), or even go for core.typed if that is your thing. I started to rely on spec more and more and I believe that coupled with pre/post conditions, it is way more useful for larger systems than static typing.

One other factor which is often forgotten in these discussions, because so few languages get there: please remember when "choosing" a language, that Clojure can be paired with ClojureScript on the frontend side. That means you can use the same data structures, same transport format (no JSON or XML quirks, ship EDN over websockets using transit and sente) and much of the same data model code on both sides. This is a huge advantage if you can make use of it. And guess what, your spec definitions work just as well on the client side, too.

Re:Clojure at Netflix (2013) [slides] by KingMob on Wednesday October 31, 2018 @10:37AM (#18346025)
I use Clojure in the large, and the lack of static typing isn't something I miss. FWIW, spec allows you make similar guarantees if you use it (technically more, since you can express more than HM type systems).

I think the static/dynamic divide speaks to deep divisions in programmer personalities, but if you're open to suggestion, I would put it like this: As Haskell/OCaML are to static langs like Java, Clojure is to dynamic langs like Python/Javascript.

Clojure has a deep vein of pragmatic simplicity throughout its core libraries and community that I think does more for taming large-scale development than static typing does, but that just my $.02.

Re:Clojure at Netflix (2013) [slides] by jwhitlark on Wednesday October 31, 2018 @01:20PM (#18347565)
In my experience, static typing was somewhat helpful in monolithic projects, but when I saw it used in heterogeneous distributed systems, it seemed to lose much of its benefit and impose new costs.

I'm curious if we're going to see a big new synergy between Kubernetes (represent everything as data) and Clojure (great with data).

Re:Clojure at Netflix (2013) [slides] by tanin on Wednesday October 31, 2018 @01:59PM (#18347898)
Is Spec simply a test? Or is it something more? I keep hearing about it, but it seems to be more than just a test case.

Let's take a simple example. I have an object with the method named "get". But I call "fetch" in my code. When will I see this error? During compile time or run time?

Re:Clojure at Netflix (2013) [slides] by lgessler on Wednesday October 31, 2018 @02:16PM (#18348040)
Think of it this way: spec gives you an easy way to ensure that your data conforms to arbitrary predicate functions.

Suppose you have an XML format, <library> that has books and authors[1]. The elements look something like this: <book author-id="0000"/>, <author id="0000" name="Plath"/>. Obviously, you want to make sure that book's author-id attribute will always refer to an author that actually exists. This is something a (Java-style) static type system can't do: it doesn't know at compile-time what the contents of a variable will be. But spec can do this because it is a runtime check[2]: you'd just write a function that ensures all author-ids refer to extant authors and register it with spec, telling it that this must be true for valid <library>s.

As for your example, I don't think that is a use-case that spec was intended to handle. You could write a spec to ensure that an object has certain properties/methods, but I'm not spec would be too useful for a function invocation on that object.

[1]: For a worked out example of this in spec, see a blog post I wrote: https://lgessler.com/posts/2018-07-12-choosing-the-right-too...

[2]: It sounds expensive, but there's a compiler flag that lets you turn off all spec checks, so you can have them only run in dev builds if you like.

Re:Clojure at Netflix (2013) [slides] by matt-noonan on Wednesday October 31, 2018 @05:14PM (#18349334)
That blog post was a nice read, thanks!

I realize this is just one example, but you might be interested to know that I've done some work making this kind of invariant enforceable via static types (though in practice, you need a combination of features that aren't found in many mainstream language besides Haskell afaik).

See the README here for motivation and some examples (in Haskell, but hopefully the idea is still clear): https://github.com/matt-noonan/justified-containers

Or the tutorial module here: https://hackage.haskell.org/package/justified-containers-0.3...

Or this paper, if you really want to go off the deep end: http://kataskeue.com/gdp.pdf

Re:Clojure at Netflix (2013) [slides] by CMCDragonkai on Thursday November 01, 2018 @12:21AM (#18351436)
I found your explanation of the relationship between an existentially quantified type and rank 2 polymorphism to be quite illuminating. Do you have any ideas why Haskell didnt have the direct way to express existential quantification in the type signature? Is it a technical limitation or something else?
Re:Clojure at Netflix (2013) [slides] by matt-noonan on Thursday November 01, 2018 @05:49PM (#18358803)
I'm not sure about the history. It looks like UHC supported existential types at some point, but I'm not sure how that would interact with what I am presuming is a more sophisticated system in GHC Haskell.

This post had some interesting tidbits about technical challenges, but I didn't fully grasp everything on first read: http://blog.ezyang.com/2016/04/hindley-milner-with-top-level...

I really like tome's observation in the other comment: universally-quantified types have a direct encoding in System F as type abstractions, but there is no such direct encoding for existentially-quantified types.

Re:Clojure at Netflix (2013) [slides] by tome on Thursday November 01, 2018 @12:55PM (#18355971)
I don't have a good answer but I did write something about this once http://h2.jaguarpaw.co.uk/posts/exists/
Re:Clojure at Netflix (2013) [slides] by vram22 on Wednesday October 31, 2018 @03:38PM (#18348656)
>Suppose you have an XML format, <library> that has books and authors[1]. The elements look something like this: <book author-id="0000"/>, <author id="0000" name="Plath"/>. Obviously, you want to make sure that book's author-id attribute will always refer to an author that actually exists. This is something a (Java-style) static type system can't do: it doesn't know at compile-time what the contents of a variable will be. But spec can do this because it is a runtime check[2]: you'd just write a function that ensures all author-ids refer to extant authors and register it with spec, telling it that this must be true for valid <library>s.

So is it similar to or different from assertions?

Re:Clojure at Netflix (2013) [slides] by tanin on Wednesday October 31, 2018 @05:36PM (#18349471)
Thank you. I think I have a good idea what it is now. It's a constraint of a field. But it goes a bit further to ensures that, for example, an author id refers to a valid entity.
Re:Clojure at Netflix (2013) [slides] by fazzone on Wednesday October 31, 2018 @02:08PM (#18347981)
In Clojure unresolved names are a compile failure, and Spec would not be involved. Spec is just a nice library for writing runtime assertions about data. An example would be calling a third-party API and then validating the response with Spec to make sure it matches your expectations, or even validating the responses you are about to return from your own API. Similar to a type system the Spec can also serve as a sort of documentation about what's in the data structure.
Re:Clojure at Netflix (2013) [slides] by CyberSun on Thursday November 01, 2018 @06:30AM (#18352547)
You'd see it at run-time, but most likely at test-time. Which is not a "time" we often think about.

Spec is designed so that you setup automated generative tests on your specced functions. These perform brute force search of the input space.

So, they won't give you FOR ALL guarantees, but will still catch quite a lot. For functions with small input domains, it would actually prove FOR ALL.

The trade off is that, you can test for much more. You can test for properties of the values, not just type. Like say making sure that the output is always smaller than the input. Or that the input never is a blank or empty string.

A downside, it doesn't work well for unpure functions. Since the generative brute force doesn't have a way to brute force the side effect, or assert properties about it. For those, you'd need to write your own tests.

All in all, don't expect it to be at all like a static type checker. It's a very different beast, which you'll want to use very differently, and which offers a very different value proposition. For example, you could want to use Spec even if you had a static type system. Just like people still write tests. Spec is a new kind of tool that can be leveraged to mitigate software defects.

Re:Clojure at Netflix (2013) [slides] by ahjones on Wednesday October 31, 2018 @02:12PM (#18348010)
This is spec: https://clojure.org/about/spec

All spec errors turn up at run time. See the guide for more details: https://clojure.org/guides/spec.

You can combine spec with test.check (Clojure's quick check library) https://clojure.org/guides/spec#_generators

Re:Clojure at Netflix (2013) [slides] by fulafel on Thursday November 01, 2018 @01:48AM (#18351701)
It's a way to describe the shape of data, a bit like schema but focus on non-exhaustive description and composability. The practical uses can be generative tests, is-a queries, conformity tests, "describe deviations from spec" queries, etc. You can also write spec for diffs in data structures, so you can use it for state changes. It's pretty open-ended.
Re:Clojure at Netflix (2013) [slides] by jwhitlark on Wednesday October 31, 2018 @05:30PM (#18349441)
At its core, spec lets you ask "Do I have what I need to do my job?", and if you have more, spec doesn't care. Type systems answer the question "Do I have exactly the machine primitives I expected, no less and no more".

It's about inspecting and verifying minimums at runtime.

Re:Clojure at Netflix (2013) [slides] by jrochkind1 on Wednesday October 31, 2018 @02:10PM (#18347989)
I believe you (I'm a ruby programmer, so you know I'm not strict about typing), but can you clarify -- `spec` is runtime, not statically typed, true?
Re:Clojure at Netflix (2013) [slides] by KingMob on Wednesday October 31, 2018 @04:26PM (#18348997)
Yes, spec is a run-time tool. It's capable of multiple things, but it's mostly used at dev-time to validate fn inputs and auto-generate spec-valid data for testing. It's usually disabled on production except at the edges of the system, where it frequently validates external input (e.g., making sure a JSON input conforms to an expected spec).

Clojure's Schema is similar to spec, if a little different. There's also Typed Clojure, which allows you to introduce gradual typing to a Clojure code base and get compile-time static typing (though I believe it's still alpha/beta quality).

Re:Clojure at Netflix (2013) [slides] by bgorman on Wednesday October 31, 2018 @04:19PM (#18348958)
I used to be a professional clojure developer, now I write in ReasonML (syntax change of Ocaml) for work.

I would say that that the reason type errors do not hurt Clojure as much as say Javascript is because Clojure data is immutable, and all the common functions operate on collections or sequences. Basically, if you write idiomatic code, you constantly use datatypes that implement the same interface which higher order functions (map, filter, reduce, etc) expect. In contrast, in OCaml for example you have to think whether you are dealing with an Array or a List. Clojure also provides mechanisms to enforce that functions (Macros too with clojure.spec) are applied the correct data parameters (clojure.spec + pre/post conditions). Spec shines when you are integrating with "foreign" data like the results of a HTTP request.

In my experience, it is faster to make changes to a project, then test quickly with the clojure repl than it is to spend a lot of time refactoring types. This is especially true when you are experimenting with how to process data a different way... Having to have all types work out at compile time is a real time sink, when mentally you already know how you want to process data.

Re:Clojure at Netflix (2013) [slides] by blandflakes on Wednesday October 31, 2018 @10:42AM (#18346070)
There are a lot of people who claim they're more productive in Clojure, in spite of or because of its dynamic typing.

I'll just throw my anecdatum out here: I found that dynamic typing was a major pain even on my own personal projects. I also never found REPL-driven development to mesh well with my workflow.

In Scala, for example, entire classes of error that I just shouldn't be able to make don't exist. Maybe I'm just the kind of person who works better with static typing.

Clojure overall is an excellent language with really good features. It just won't get out of my way sometimes.

Re:Clojure at Netflix (2013) [slides] by xapata on Wednesday October 31, 2018 @11:35AM (#18346574)
Try to grok REPL-driven development before dynamic typing. You won't like the latter until you love the former.
Re:Clojure at Netflix (2013) [slides] by jerf on Wednesday October 31, 2018 @12:25PM (#18347073)
Haskell does REPL-driven development fairly well. It's not the best REPL, but it works, especially if you take a moment to learn a few of the commands to drive it. Dynamic types are not necessary for REPL-driven development.
Re:Clojure at Netflix (2013) [slides] by yogthos on Wednesday October 31, 2018 @08:00PM (#18350291)
Haskell REPL is a completely different animal from the one in Clojure, and it's a stretch to say that you can do REPL driven development in Haskell.

What people mean by REPL driven development in context of Clojure is that the REPL is running within the context of the application you're developing. The editor is connected to the REPL, and you can modify any part of the application at runtime. You can even connect the editor to a REPL on a remote machine such as a production server and inspect its state from your editor.

Re:Clojure at Netflix (2013) [slides] by CMCDragonkai on Thursday November 01, 2018 @12:27AM (#18351457)
I do all my Haskell development in a repl driven way. It's even better than Python's repl due to the way it loads modules and allows reloading quickly and automatically checks the type signatures.

Having the entire application loaded into your repl is even more powerful. I did it once in PHP using psych and was able to make use of any controller and try parts of it out. The main thing that is necessary is the ability of the REPL to either drive change to the code, or react to changes on file (the former is temporal, the latter is more permanent) and perform the necessary incremental computation to become eventually consistent.

Re:Clojure at Netflix (2013) [slides] by xapata on Wednesday October 31, 2018 @12:59PM (#18347381)
Yes, that's why becoming happy with REPL-driven development is more important than becoming happy with dynamic typing. I think much of the productivity boost attributed to dynamic typing is in fact caused by heavy use of a REPL, and not dynamic typing.
Re:Clojure at Netflix (2013) [slides] by blandflakes on Wednesday October 31, 2018 @06:13PM (#18349677)
I'd maybe consider that perhaps becoming happy with REPL-driven development isn't a universal solution. It's not like I didn't understand the concepts, the tools, or the process. I just didn't find that it made me any more proficient at building software.
Re:Clojure at Netflix (2013) [slides] by xapata on Wednesday October 31, 2018 @08:15PM (#18350391)
Of course that's possible, but the evidence suggests that most people become more productive when using the REPL to experiment while developing large projects.

One issue with trying to adopt the habit midway through a project is having a design that makes it easy to load small portions of the project with test data into the REPL. It's not quite the same as having code that's easy to test.

Do you use an interactive debugger? It's the same principle, but earlier in the lifecycle of development.

Re:Clojure at Netflix (2013) [slides] by blandflakes on Wednesday October 31, 2018 @08:34PM (#18350489)
I appreciate your earnest approach, but I also feel somewhat patronized. I've been doing this for a while. I used Clojure extensively for years, was productive and found much to like. The REPL wasn't part of it, and I don't miss it. I still use Clojure on occasion.

This response is pretty common: "You probably weren't doing it right. Trust me, this is the way!" While I agree that suspending beliefs/old practices and trying new things is important when using new technology, I'm also pretty confident that I gave Clojure a more than fair shake, and just find that I don't get much out of the technology surrounding the REPL in that environment.

I was more productive in Scala in days than I was after years of using Clojure, and the difference between what I'm saying and your response is that I'm not claiming my experience is universal.

Re:Clojure at Netflix (2013) [slides] by xapata on Wednesday October 31, 2018 @09:32PM (#18350785)
No condescension meant. It's hard to estimate how experienced someone is from a few sentences in the forum. I'm generally careful to make assertions about most and in general rather than all or universally.
Re:Clojure at Netflix (2013) [slides] by blandflakes on Wednesday October 31, 2018 @09:36PM (#18350799)
None taken personally - I just find it valuable when I read comments against the grain, and had an experience that I know many Clojurists find atypical and wanted to share that my style of work just doesn't lend itself to Clojure as supported today.
Re:Clojure at Netflix (2013) [slides] by bribri on Thursday November 01, 2018 @01:10AM (#18351597)
Building up complex types in the REPL is hard compared to using a map...
Re:Clojure at Netflix (2013) [slides] by ken on Wednesday October 31, 2018 @12:45PM (#18347239)
It sounds like Clojure was too much "out of your way", and you prefer a language like Scala that gets in your face about possible issues sooner.
Re:Clojure at Netflix (2013) [slides] by blandflakes on Wednesday October 31, 2018 @06:14PM (#18349678)
I can get on board with this phrasing.
Re:Clojure at Netflix (2013) [slides] by Rapzid on Wednesday October 31, 2018 @12:50PM (#18347287)
Never did much clojure outside Riemann "configs" but I found the experience of light table's insta-repl fantastic. Scratch pad type workflows are a fav of mine(linqpad is amazing) and insta-repl blew me away.
Re:Clojure at Netflix (2013) [slides] by keymone on Wednesday October 31, 2018 @11:48AM (#18346704)
> I also never found REPL-driven development to mesh well with my workflow.

> In Scala, for example, entire classes of error that I just shouldn't be able to make don't exist.

guess what programming practice allows to almost entirely avoid those classes of error?

Re:Clojure at Netflix (2013) [slides] by CyberSun on Thursday November 01, 2018 @06:21AM (#18352507)
Have been using Clojure for three years on a dev team of around 10 people. We haven't had any issue with maintenance and extensibility of the code base over time. I'd say it is as easy to maintain and extend as it was in the beginning.

That said, we have a micro-service architecture. Thus the overall system grows horizontally, in that more and more components are built and developed. So individual components don't really grow that large. This would be different say to building a giant monolith like a AAA game, or a massive application like Photoshop. So I can't speak to how such a code base would scale.

Also, this might sound strange, but I don't think we've ever had to refactor the code base in those three years. The paradigms of Clojure (data driven/functional/immutable/meta), combined with our architecture, it just doesn't really need you to refactor things to add features.

Most adding of features tend to be... Write functions for it. Plug the functions inside the outer orchestration chains of function composition which dictates code flow. Add some more keys to our data-structures. Write tests. Release. Where as in Java, we used to have to shuffle all the Class/Interface arrangements around all the time to make room for new features.

Re:Clojure at Netflix (2013) [slides] by saosebastiao on Wednesday October 31, 2018 @10:42AM (#18346069)
I've used all three, and at least in my opinion, Scala is the most production ready by miles.

Clojure is great if you like dynamic typing...by far the best dynamically typed functional language out there. Beware of claims that clojurescript and clojure are the same language. While both are dynamically typed, clojure's type discipline is strongly enforced (type errors will throw exceptions) while clojurescript will fail silently. I personally know of a team that experienced a million dollar bug because of this subtle distinction...you absolutely have to be prepared for it if you plan on using both. If you like gradual typing, there are some great options.

Haskell is good but the language's strictness (especially around effects) is very demanding. This can be both a good thing and a bad thing. In my experience/opinion, it's a good thing in some use cases and a bad thing in other use cases...but there's no way to opt out of it when it's a bad thing. Further, the ecosystem is far less useful than the jvm ecosystem.

Scala definitely has its faults, but its type system is extremely expressive without being extremely restrictive. I've never felt restricted by it (like I did with Haskell), but it regularly blows me away how easy it is to refactor and fix errors. The Java interop is far more intuitive than Clojure. Drawbacks: The only really useable IDE is Intellij. Some libraries tend to form an ecosystem of dependencies which don't play well with other ecosystems (ie some libraries are "Scalaz-only", some are "Cats-only", etc), which can be really annoying at times. Java interop isn't extremely straightforward in the case of collections (if that is a big concern, go with Kotlin which is phenomenal).

Re:Clojure at Netflix (2013) [slides] by didibus on Thursday November 01, 2018 @07:05AM (#18352738)
ClojureScript is strongly typed. I'm curious to know the specific case they ran into? I suspect it might have been interop with JavaScript, because you shouldn't have weak types in ClojureScript.
Re:Clojure at Netflix (2013) [slides] by darksaints on Thursday November 01, 2018 @06:48PM (#18359162)
No, it's not. Try `(+ 1 "1")` in clojure and clojurescript. In clojure, it produces a runtime exception. In clojurescript it silently errs, resulting in a "11".

The compiler correctly infers and warns of the problem, but that is of no use if your inputs change types on you. In the case of the service I'm referring to, an external service started serving up json strings for large numbers, whereas it had previously been json numbers. Since there was no exception produced, it silently corrupted data for a couple weeks before it was caught.

Re:Clojure at Netflix (2013) [slides] by didibus on Monday November 05, 2018 @05:09AM (#18381144)
I asked about this here: https://clojureverse.org/t/clojurescript-is-weakly-typed/311...

And documented to what extent types are automatically coerced.

Also got the rational behind this: performance.

And that every new release of the compiler performs smarter and smarter type inference and can warn about more of these cases. Though it still misses some as of now.

Re:Clojure at Netflix (2013) [slides] by ajss on Friday November 02, 2018 @11:47AM (#18363859)
People were running a real money service that consumed JSON from another service and it didn't validate the incoming JSON? Wow.
Re:Clojure at Netflix (2013) [slides] by saosebastiao on Friday November 02, 2018 @06:55PM (#18367396)
The problem comes from the fact that clojure and clojurescript are two different type disciplines, but people approach them with the same expectations. That exact problem would have errored out if they were running clojure, and a thrown exception is good enough validation for many developers. But because they were duped into a false safety net by thinking clojure == clojurescript, it slipped right underneath their radar.
Re:Clojure at Netflix (2013) [slides] by didibus on Friday November 02, 2018 @03:44AM (#18361266)
Woa, you're right. I had never realized they were compiler warnings and not JS errors. Thanks for the tip.
Re:Clojure at Netflix (2013) [slides] by justinhj on Wednesday October 31, 2018 @02:06PM (#18347962)
Scala is sometimes referred to as the Haskellator, in that once you get far enough down the pure functional programming road, you may find yourself wanting to switch to Haskell. Scala doesn't enforce pure functional programming as much as Haskell. On the other hand I believe that Scala is much more widespread in industry because it's easy to sneak it in the door as an OOP/imperative language that just happens to have good support for higher kinded types, immutable data and other fp features. My advice, if it's for your own personal learning and possibly for some small in-house projects learn Haskell. If you want to use it for a large project with developers that are interested in learning fp but not yet fully committed learn Scala.

Also, yes, any dynamic language suffers from difficulties with refactoring, runtime errors, needing lots of test coverage and not scaling well to large teams and codebases. IMHO

Re:Clojure at Netflix (2013) [slides] by jwhitlark on Wednesday October 31, 2018 @06:01PM (#18349616)
After my time with Scala, I think that if I need a heavy type system, I'd look to Haskell. And there are certainly classes of problems that I would choose a type system, but the general case isn't nearly as strong as I though it was before seeing them used in anger.

So, in the following, I'm not ranting at you, but I am saying I see lots of arguments for types that jump to QED without nearly enough evidence, with the "everybody knows" kinda explanation. And you happen to have put a handy list right in front of me when I had time to respond.

> ... difficulties with refactoring, runtime errors, needing lots of test coverage and not scaling well to large teams and codebases.

I was really impressed with Scala when learning or toy projects, but every time I saw it in production it was pretty horrid. Small, technical refactoring was taken care of by the IDE, but large scale refactoring takes the same amount of thought as before, but now there's 10x as much code to understand first.

There were less runtime errors, but not much less, and I saw a lot of code that didn't error, but didn't get done what it needed to.

When it comes to scaling to large teams, I saw lots of "The compiler says it's ok, and the tests pass, so commit!" where the code didn't make sense! The old thing about write code for programmers to understand first, and the machine to understand second...

Outside of certain specific situations, I just haven't seen the benefits outweigh the increased code weight and slower time to market.

Re:Clojure at Netflix (2013) [slides] by justinhj on Saturday November 03, 2018 @01:54PM (#18371509)
Just like your response, my evidence is the anecdata of my own experience. I’ve built a large system that 9 engineers use, all of different experience levels. Functional programming gives more understandability not less, you can always understand the input and our output of a function from the types. I really can’t see how anyone can argue about types and refactoring ... even without any tools you can just change the function signature and get everything compiling again. You know you didn’t miss any callers. Even better one knows when you can change a data structure or effect type from one to another because the compiler will tell you. If you use scala correctly you can eliminate whole classes of runtime errors and i’ve experienced that too both in a good way and a bad way when we let nulls creep in.

Whats even better is higher kinded types let you assert semantics to engineers that are hard to do in Clojure.. this type must be appendable or foldable, this type must handle async, this type maybe missing and this list cannot be empty.

All these things are a joy not a burden once they start working for you

Re:Clojure at Netflix (2013) [slides] by bribri on Thursday November 01, 2018 @01:08AM (#18351582)
This is the number one opposition to Clojure I hear. I was strongly in the static typing camp and rejected Clojure at first, even compared to Java and C++. I tried Haskell and found it amazing, but still too complex. After knowing more about functional programming I returned to Clojure and it finally clicked. I feel writing extremely simple code quickly and reinvesting that time into writing tests was a better workflow than any typed language I've used. I've only had a good experience in the REPL using emacs, but when you set it up properly, it's amazing.
Re:Clojure at Netflix (2013) [slides] by didibus on Thursday November 01, 2018 @07:03AM (#18352722)
That's my experience as well.

Static types are overrated. Okay, so you caught the obvious errors a few minutes earlier, but while you were wrestling with types, I ran 30 quick tests validating the behavior, and made them unit tests.

I've only ever had trivial type error bugs, all the nasty ones are behavioral or related to race conditions, or at the boundaries where types don't exist.

Re:Clojure at Netflix (2013) [slides] by zumu on Wednesday October 31, 2018 @01:50PM (#18347810)
> Main contenders so far are Haskell, Scala and Clojure (Reason might go on the list soon too)

How about plain old OCaml? Jane Street seems to find some success with it.

Re:Clojure at Netflix (2013) [slides] by nestorD on Wednesday October 31, 2018 @02:56PM (#18348333)
Or, also in the ML family, F# ?
Re:Clojure at Netflix (2013) [slides] by bgorman on Wednesday October 31, 2018 @04:06PM (#18348854)
Reason and OCaml are the same language
Re:Clojure at Netflix (2013) [slides] by gregwebs on Wednesday October 31, 2018 @10:29AM (#18345971)
You can't refactor Clojure without fear like in Haskell. It is actually possible in Haskell now to defer type errors to runtime, but I haven't heard of a practice of using that, even to aid in refactoring.

See this comment: https://news.ycombinator.com/item?id=18345672

Re:Clojure at Netflix (2013) [slides] by akmiller on Wednesday October 31, 2018 @10:39AM (#18346045)
I'd disagree with this.

Primarily been developing with Clojure for 5 years now with some pretty large codebases. It does depend on how you write your code but favoring pure functions, pushing immutability to the edges of your programs allows you to refactor without fear.

Clojure also has many things to aid in this such as pre/post conditions, clojure.spec (which allows you to build complex type definitions), and of course test.check (property based testing).

Re:Clojure at Netflix (2013) [slides] by lgas on Wednesday October 31, 2018 @11:08AM (#18346290)
Having spent the last two years working professionally in Haskell after having spent the previous two years working professionally in Clojure, I'd disagree with this. Haskell is roughly infinitely better than Clojure for meaningful refactoring. Clojure gives you (great) tools for using your own brain to make sure the refactor goes well. Haskell replaces your brain almost entirely in the process and just gives you a precise list of the things have to fix. The difference in experience is night and day. When you're done with a Haskell refactoring (even a major one) and it compiles again you will be completely done 99% of the time. With Clojure there's not even a "compiles again" moment to look for, and even when you think you're done you're refactoring there's a good chance that there's something that will pop up at runtime (and some point arbitrarily far in the future) and tell you otherwise.
Re:Clojure at Netflix (2013) [slides] by ken on Wednesday October 31, 2018 @01:54PM (#18347847)
Just as with the word "developer" [1], I'm beginning to think that the word "refactor" is a dirty word. Are we even talking about the same activity? How is it people are alternately claiming that Haskell and Clojure (virtual opposites on the language spectrum) are infinitely superior to each other at the same thing?

Wikipedia defines it as "the process of restructuring existing computer code without changing its external behavior", which sounds about right, but it's also so completely generic that it could mean almost anything.

Claiming that Clojure can't do 'meaningful refactoring' sounds to me about like claiming that Kanji is bad for transcribing Welsh, or that sign language doesn't work well for audiobooks. They're technically languages but the fundamentals are so different that all the comparisons are talking right past each other.

The style of refactoring that one does in Haskell (disclaimer: it's been many years since I've written any) is not really possible in Clojure, but it's not really necessary, either.

[1]: https://news.ycombinator.com/item?id=18310958

Re:Clojure at Netflix (2013) [slides] by Jach on Wednesday October 31, 2018 @05:47PM (#18349542)
"Refactoring" has a definitive origin, which is Martin Fowler's Refactoring book from 1999.

Fowler wanted to write the book using Smalltalk, but because the techniques he wanted to write about were fairly language agnostic, he did it in Java, as that was more popular. Unfortunately I think a lot of programmers missed the point, and now think refactoring is only something that can be done well in languages like Java (static typing) and with IDE support.

He's announced a second edition[0] in which he'll use JavaScript, to again prove the point that the techniques matter less than the language (other than sometimes techniques that work for class-based designs aren't as relevant as for function-based designs) and because JS is so popular. I'm not optimistic it will help anyone see the underlying point, but at the very least it might kill the idea that refactoring has to be hard in non-static-typed languages.

[0] https://martinfowler.com/articles/201803-refactoring-2nd-ed....

Re:Clojure at Netflix (2013) [slides] by wild_preference on Wednesday October 31, 2018 @03:32PM (#18348615)
I'd say it comes down to the more standard fare of dynamic vs static typing where the latter has a general advantage in refactoring.

It's true that Clojure has its own perks that aid in refactoring, like how you may have less code altogether. But in my experience it's still a drop in the bucket compared to the zoomed out view of dynamic vs static typing.

For example, could you give some examples that make Clojure particularly good in this regard? I'd have trouble coming up with many that quantify favorably against compile-time analysis. And it would be unfair to take this reality and say "Clojure sucks at refactoring." I think that people do say that is what clouds these discussions where one then needs to point out that Clojure has advantages over other dynamically typed languages which is definitely true.

Re:Clojure at Netflix (2013) [slides] by lgas on Wednesday October 31, 2018 @03:20PM (#18348535)
I never claimed that Clojure can't do meaningful refactoring, and in fact I acknowledged that Clojure gives you great tools for using your own brain to do refactoring. Clojure is my favorite dynamic language and I heartily recommend it to anyone looking for something in that space. It's just that the experience of refactoring (or anything like it -- even just making large changes) in Haskell is massively better than the experience of doing it in Clojure.
Re:Clojure at Netflix (2013) [slides] by didibus on Thursday November 01, 2018 @07:19AM (#18352818)
> The style of refactoring that one does in Haskell (disclaimer: it's been many years since I've written any) is not really possible in Clojure, but it's not really necessary, either.

You're spot on. It just isn't really necessary to refactor Clojure code. In my many years using it professionally, I didn't have to refactor it once. The code just stays clean, it doesn't need cleaning up. So I find the whole refactoring argument moot. Why does your language lend itself to code that needs to be refactored? Sounds like a disadvantage to me.

Re:Clojure at Netflix (2013) [slides] by akmiller on Wednesday October 31, 2018 @12:44PM (#18347229)
To be fair, I wasn't comparing the two. I've never used Haskell (for more than just learning/tutorials).

I would suggest that if you have runtime bugs popping up in Clojure programs then that would suggest the inputs to functions (since they should be primarily pure) are not being validated which can easily be accomplished. I would imagine this needs to be done in Haskell as well since just verifying types does not indicate valid data.

Re:Clojure at Netflix (2013) [slides] by matt-noonan on Wednesday October 31, 2018 @03:16PM (#18348501)
In Haskell designs, developers tend to be quite careful about setting things up so that verifying types does indicate valid data. Coming from other languages (C++, Common Lisp, and Python in my case), it can be a little surprising just how often and how easily you can make this happen.
Re:Clojure at Netflix (2013) [slides] by akmiller on Wednesday October 31, 2018 @04:09PM (#18348874)
You can do similar things with spec for Clojure. As I stated in another response that is opt-in obviously so it requires more discipline perhaps but it is definitely available.
Re:Clojure at Netflix (2013) [slides] by willtim on Wednesday October 31, 2018 @04:34PM (#18349073)
Null checks are also opt-in, that's why Tony Hoare calls null his "Billion Dollar Mistake". Explicit runtime checks are in no way "similar" to static typing! Static typing is a machine-checked proof of correctness for a certain class of properties.
Re:Clojure at Netflix (2013) [slides] by matt-noonan on Wednesday October 31, 2018 @04:36PM (#18349099)
I'd rather have the compiler ensure that my discipline (and my team's) never slips, and to get guarantees about all possible executions instead of just those executions that have been tested.

For these reasons and others, I don't personally find run-time checks to be an adequate replacement for compile-time checks. But there is no really convincing research on the subject, and I certainly don't begrudge your preference here.

Re:Clojure at Netflix (2013) [slides] by lgas on Wednesday October 31, 2018 @03:42PM (#18348675)
> To be fair, I wasn't comparing the two.

Well, the statement you were disagreeing with from the post you responded to was:

> You can't refactor Clojure without fear like in Haskell.

You go on to suggest you can achieve a similar experience in Clojure by "depending on how you write your code". This simply hasn't been my experience. Just "writing your code the right way" solves almost every problem that arises in programming, but just isn't always feasible in practice (on a team of developers with mixed skill levels, operating under deadlines, etc).

Re. validation, as Matt Noonan mentioned, in Haskell the goal is often to build/leverage correct-by-construction data types which obviate the need for any validation.

A simple example of this would be the `NonEmpty` (list) type.

If you have a function that pulls a list out of some key in a clojure map and it's intended that it always be a non-empty list you still need to check if actually is or not before using it because you have no control over what the caller passes to you. If the caller never sends an empty list you're fine, but if they do and you don't check for it, you've got a bug. Even if you do check for it, there's often nothing sensible you can do at that point since the local function shouldn't know anything about it's calling context, so you have to raise an error or return a nil or something.

On the flip side, in Haskell instead of using a map you would be likely to create a specific data type, and in that data type you would declare the non-empty field to be of the `NonEmpty` type. The first immediate benefit you get is that you no longer have to do any of these checks for the list being empty (or nil, or something else instead of a list) and instead just write your algorithm over the list. Among other things this results in cleaner, simpler, and less code in your immediate function.

But there's another benefit which is now anyone that calls your function has to have constructed a `NonEmpty` list before they call your function. The impact of this essentially naturally propagates the need to construct that `NonEmpty` list to the right place in the code. Maybe it really is just the caller to your your function that needs to take a regular (possibly empty) list that it has and package it up into a `NonEmpty` to call your function... and in that case you still get the benefits of code that shorter, simpler and more clearly communicates it's intention, but where this really shines is when that requirement to pass a non-empty list makes you realize something about the nature of your problem, and you let that `NonEmpty` propagate all the way out towards the boundaries of your application.

Then you end up in a situation where a) all of the code that touches that field anywhere is simpler, clearer, etc. but more importantly b) if someone does send you malformed data with an empty list (say via JSON over a web API or similar) then the code that deserializes the JSON into the `NonEmpty` will fail and you will get an error that says something like "Couldn't decode a YourCustomType from {whatever it was trying to decode}" at the very moment that the bad data tried to enter your system -- instead of just reading the JSON into a map because it was well formed and then letting the record with the empty list in it bounce around until it hits a function that assumes it's non empty at which point you may have little to no information about the provenance of the data or other details that would make easier to solve the problem.

Re:Clojure at Netflix (2013) [slides] by akmiller on Wednesday October 31, 2018 @04:07PM (#18348861)
Exactly, and I was responding to the fact that I never fear refactoring my Clojure code.

Your examples of the type safety can all be mimicked with spec in Clojure. Granted spec is opt-in (but I'm guessing so is some of the more detailed type safety attributes you are talking about like NonEmpty).

Anyhow, to each their own and one persons experience isn't likely to be the same as the others so I'd encourage everyone to try out many languages. Some languages click with people more than others do so it's always worthwhile to experiment.

Re:Clojure at Netflix (2013) [slides] by lgas on Wednesday October 31, 2018 @07:57PM (#18350265)
The opt-in vs opt-out nature is definitely key. As an example, if you write a small function to operate on a non-empty list and use it on a non-empty list in exactly one place, everything is fine in either language. But in Haskell if someone else tries to use the function passing in an empty list, it won't compile, they will encounter the problem right away. In Clojure if someone sees the function and says "oh good, someone's already written this function, I'll just use it" and doesn't realize that it expects a non-empty list, then you now have another situation where something is going to explode when data that contains an empty list arrives. And the error is again going to be far (in the code and in time) from the source of the problem. The difference between the experience of "I have good tools that I can use effectively to arrange to avoid bugs" and "I have tools that simply will not allow entire (large) classes of bugs at all" is what I'm really trying to get at. This is just an area where Clojure shines compared to most dynamic languages but Haskell shines compared to nearly all languages.
Re:Clojure at Netflix (2013) [slides] by willtim on Wednesday October 31, 2018 @04:26PM (#18348998)
Explicit runtime checks that must be manually added are not a substitute for static typing! You should follow your own advice and try out a statically typed language with good inference!

Every tool has a sweet spot and large code bases and maintenance is well outside the sweet spot of any dynamic language.

Re:Clojure at Netflix (2013) [slides] by jwhitlark on Wednesday October 31, 2018 @06:40PM (#18349809)
Well, there are certainly classes of problems that I'd use static typing for, but in my experience, static typing != software that gets the job done. That doesn't mean that it holds things back, though.

Many static typing arguments remind me of the Air Force's old "We'll bomb them so hard we won't have to send in ground troops." I just haven't seen it in practice, and the few studies that have looked at it empirically haven't seen a clear advantage either. If you know of such a study, please point it out!

In the end, all sorts of combinations have succeeded or failed, to the point where now when people start talking about "the right tool for the job", I add in "the right tool for the right people in the right environment for the right job..."

Re:Clojure at Netflix (2013) [slides] by matt-noonan on Wednesday October 31, 2018 @09:13PM (#18350683)
> I just haven't seen it in practice, and the few studies that have looked at it empirically haven't seen a clear advantage either. If you know of such a study, please point it out!

This is basically right; there are not a ton of studies, and the ones that exist mostly have pretty bad methodologies. Dan Luu summarized a bunch of them, circa about 2014. [1]

Since then, there has been one study in this area that I think has a solid, well-defined, and plausible methodology [2]. Plus a "Threats to Validity" section, sorely missing from many other papers in this area. They work from a corpus of real-world public bugs in Javascript programs and quantify how many are detected by simple type annotations via TypeScript and Flow. They cap the amount of time for trying to resolve a bug with type annotations at 10 minutes. The result is that over a corpus of 400 bugs, they were able to resolve about 60 using either of TypeScript or Flow, suggesting that 15% of Javascript bugs can be eliminated by using either type system.

That's not a huge difference, but it isn't trivial either. The authors quote an engineering manager at Microsoft: "That’s shocking. If you could make a change to the way we do development that would reduce the number of bugs being checked in by 10% or more overnight, that’s a no-brainer. Unless it doubles development time or something, we’d do it."

I suspect with languages that are more amenable to static types the results would be even better, but there is no solid research that I know of to back that up.

[1] https://danluu.com/empirical-pl/

[2] http://ttendency.cs.ucl.ac.uk/projects/type_study/documents/...

Re:Clojure at Netflix (2013) [slides] by jwhitlark on Wednesday October 31, 2018 @10:04PM (#18350937)
My gut feeling is the problem matters more than the language. I'm hopeful with the push to microservices we'll be able to mix and match these things where we think they'll work best.
Re:Clojure at Netflix (2013) [slides] by tanin on Wednesday October 31, 2018 @02:03PM (#18347938)
> It does depend on how you write your code but favoring pure functions, pushing immutability to the edges of your programs allows you to refactor without fear.

Dynamic typing is punitive if we make mistake (e.g. using the wrong architecture, now we have to refactor), and we'll make mistakes.

Writing perfect code with perfect architecture and perfect test has been the argument "for" dynamic typing, but I don't think that's a realistic assumption.

Re:Clojure at Netflix (2013) [slides] by yogthos on Wednesday October 31, 2018 @08:11PM (#18350368)
My experience is that static typing tends to lead to higher coupling and monolithic design, while you tend to break things up more aggressively in a dynamic language like Clojure.
Re:Clojure at Netflix (2013) [slides] by tanin on Thursday November 01, 2018 @02:48PM (#18357135)
I understand what you mean, and it's definitely a trade off.

Static typing requires all types to make sense together no matter how small the addition is.

In dynamic typing, we can just add a class/object/field here and there because it'll only be used narrowly anyway. So, it's often fine.

This would be great if we can think of a small example to illustrate this trade off.

Re:Clojure at Netflix (2013) [slides] by yogthos on Thursday November 01, 2018 @03:39PM (#18357657)
One example I can think of is how Ring middleware works. The request map is passed through a series of functions, and each one can modify it in its own way. For example, you could have an authentication library that adds an :identity key, and another that adds a :session key. These libraries can work together without having to know anything about each other.
Re:Clojure at Netflix (2013) [slides] by darksaints on Wednesday October 31, 2018 @10:45AM (#18346095)
Clojure isn't nearly as bad as most would have you believe. I still prefer static strong typing, but since Clojure is strongly typed usually you'll find most of your errors within a few rounds of execution. And they have an excellent implementation gradual typing if that's your thing. If you value dynamic types, this is about as good as you can get.
Re:Clojure at Netflix (2013) [slides] by ken on Wednesday October 31, 2018 @02:00PM (#18347909)
For some reason I find it funny that all the comments here about static typing are written in English, a language lacking not only static typing, but a syntax that's possible to automatically check. It lets one say things like "I still static strong typing" (which you've since corrected)!

One could write a comment here in Haskell describing why static typing is better, yet nobody ever does. I think even the most ardent static typing proponents are implicitly admitting that dynamic typing is fine, and other concerns can be more important, in some contexts.

Re:Clojure at Netflix (2013) [slides] by Jach on Wednesday October 31, 2018 @02:31PM (#18348150)
You would probably enjoy this talk by Guy Steele where he discusses the language of mathematics that computer science people use in their papers (Computer Science Metanotation) to talk about other languages. Funnily it's untyped and lacking a rigorous specification... https://www.youtube.com/watch?v=dCuZkaaou0Q
Re:Clojure at Netflix (2013) [slides] by jwhitlark on Wednesday October 31, 2018 @06:42PM (#18349822)
There's a reason why we teach math in kindergarten and proofs in college. There's lots of things that are useful to do without the rigors of rigorous proof.
Re:Clojure at Netflix (2013) [slides] by bunderbunder on Wednesday October 31, 2018 @12:22PM (#18347031)
You can't necessarily do it in Haskell, either, on account of the whole partial functions thing.
Re:Clojure at Netflix (2013) [slides] by preordained on Wednesday October 31, 2018 @12:45PM (#18347238)
Or lack of great IDE...the best type system seems a waste without a right-click refactor option to show for it. I'm not going to chase compilation errors file by file...
Re:Clojure at Netflix (2013) [slides] by jose_zap on Thursday November 01, 2018 @02:43PM (#18357073)
It has a language server which works with many good editors like vscode, that’s something at least!
Re:Clojure at Netflix (2013) [slides] by udfalkso on Wednesday October 31, 2018 @11:14AM (#18346349)
Another option worth considering is Elixir. It's also dynamically typed, but the pattern matching helps mitigate dynamic type issues if used correctly. It's a pleasure to work with.
Re:Clojure at Netflix (2013) [slides] by jb3689 on Wednesday October 31, 2018 @11:26AM (#18346459)
I love Elixir but it's worth noting that I'd be hard-pressed to recommend it for every project because of the performance and base OS integration characteristics of the BEAM. The BEAM is a truly amazing piece of software and I do think that Erlang and Elixir are the true microservices dream we all wanted, but the BEAM uses preemptive scheduling and doing things like spinning up a new OS process can be a lot more work in Erlang than you'd deal with in other languages
Re:Clojure at Netflix (2013) [slides] by jimbokun on Wednesday October 31, 2018 @01:17PM (#18347527)
Why in the world would you spin up a new OS process in Erlang? Having its own process model is at the very core of Erlang's design, so it seems very odd to want to bypass it.
Re:Clojure at Netflix (2013) [slides] by dnautics on Wednesday October 31, 2018 @11:48AM (#18346705)
What are the performance issues? People (e.g. Instagram) deploy scaled apps in Python, which is far less performant.

Bleacher report obsesses about information push latency, and they use an all elixir stack that deploys to at least a million users.

Not to mention WhatsApp.

Re:Clojure at Netflix (2013) [slides] by lostcolony on Wednesday October 31, 2018 @12:34PM (#18347139)
CPU bound loads won't benefit from Elixir.

If most of what you're doing is a little minimal data transformation, but otherwise shuffling data to different network locations, and you want consistent latency/throughput, Elixir is a dream. If you're doing some tight number crunching, then not so much.

Re:Clojure at Netflix (2013) [slides] by dnautics on Wednesday October 31, 2018 @05:41PM (#18349501)
Exactly. Who in sv is actually doing tight number crunching all the time? I think most of what we do is shuffling data around. If you need it, you can always also just orchestrate a container with a computational task anyways. And then someone's gotta marshal the data and metadata to that container... Might as well be something dreamy.
Re:Clojure at Netflix (2013) [slides] by lostcolony on Wednesday October 31, 2018 @06:11PM (#18349664)
I'll also point out, I'd much prefer a default of Elixir, than of Java.

Why? Because most of my problems are things Elixir can solve well, and Java can solve moderately well, after a bunch of pain and tweaking. Default to the thing that can solve most of your problems well, and pick a different tool when needed, rather than default to the thing that can solve all of your problems moderately well, and you never pick anything else, and so never have any elegant, quickly built yet well designed, solutions.

Re:Clojure at Netflix (2013) [slides] by udfalkso on Wednesday October 31, 2018 @11:35AM (#18346562)
Yup, it depends what you're building I suppose. For an Elixir/Phoenix web application or api I've found the performance to be very impressive.
Re:Clojure at Netflix (2013) [slides] by dmead on Wednesday October 31, 2018 @02:57PM (#18348346)
as a long time haskell user (but no longer) i disagree that haskell is production ready.
Re:Clojure at Netflix (2013) [slides] by yogthos on Wednesday October 31, 2018 @07:54PM (#18350250)
Worked with static typing for about a decade primarily with Java in the enterprise. However, I've also used Haskell and Scala which have advanced type systems. I moved to work with Clojure about 8 years ago, and I don't miss types. If I did, I would've gone back to a typed language a long time ago.

My experience is that dynamic typing is problematic in imperative/OO languages. One problem is that the data is mutable, and you pass things around by reference. Even if you knew the shape of the data originally, there's no way to tell whether it's been changed elsewhere via side effects. Keeping track of that quickly gets out of hand.

What I find to be of highest importance is the ability to reason about parts of the application in isolation, and types don't provide much help in that regard. When you have shared mutable state, it becomes impossible to track it in your head as application size grows. Knowing the types of the data does not reduce the complexity of understanding how different parts of the application affect its overall state.

My experience is that immutability plays a far bigger role than types in addressing this problem. Immutability as the default makes it natural to structure applications using independent components. This indirectly helps with the problem of tracking types in large applications as well. You don't need to track types across your entire application, and you're able to do local reasoning within the scope of each component. Meanwhile, you make bigger components by composing smaller ones together, and you only need to know the types at the level of composition which is the public API for the components.

REPL driven development [1] also plays a big role in the workflow. Any code I write, I evaluate in the REPL straight from the editor. The REPL has the full application state, so I have access to things like database connections, queues, etc. I can even connect to the REPL in production. So, say I'm writing a function to get some data from the database, I'll write the code, and run it to see exactly the shape of the data that I have. Then I might write a function to transform it, and so on. At each step I know exactly what my data is and what my code is doing.

Where I typically care about having a formalism is at component boundaries. Spec [2] provides a much better way to do that than types. The main reason being that it focuses on ensuring semantic correctness. For example, consider a sort function. The types can tell me that I passed in a collection of a particular type and I got a collection of the same type back. However, what I really want to know is that the collection contains the same elements, and that they're in order. This is difficult to express using most type systems out there, while trivial to do using Spec.

[1] https://vvvvalvalval.github.io/posts/what-makes-a-good-repl.... [2] http://danboykis.com/?p=2293

Re:Clojure at Netflix (2013) [slides] by cutler on Wednesday October 31, 2018 @09:41PM (#18350825)
This nails it for me. Clojure has unique advantages over other, less composable languages and the design of clojure.spec is where you see it most clearly. Rich Hickey's presentation on clojure.spec demonstrates how you can define contracts for your functions which go well beyond what type systems have to offer.
Re:Clojure at Netflix (2013) [slides] by kachnuv_ocasek on Wednesday October 31, 2018 @11:11AM (#18346326)
Some implementations of SML are definitely production-ready.
Re:Clojure at Netflix (2013) [slides] by plinkplonk on Wednesday October 31, 2018 @01:20PM (#18347568)
which implementaions of SML are the production ready ones?
Re:Clojure at Netflix (2013) [slides] by dmead on Wednesday October 31, 2018 @02:56PM (#18348335)
clojure has an out of band type checker. have another look