News for hackers. Stuff that matters.
Re:A Year in Clojure by sdegutis on Sunday October 07, 2018 @09:30AM (#18160434)
> "With function specs, I could assert the inputs and outputs to specific functions at runtime; not quite as reassuring as compile-time but better than nothing!"

For 10 years I've seen many developers who have only really used statically typed languages like C++ and Java, now finding the "joy" of dynamically typed languages and all the "freedom" they give you, only to realize within a year or two that, wait a second, verifying those types at compile-time was actually pretty useful. I've also seen mass migrations away from languages like Ruby, Python and Clojure towards OCaml, Haskell, and F#, because people really enjoyed the functional programming aspects but wanted their static typing back. After writing the 10,000th unit test that only exists to ensure that you spelled some function/method/hash-key correctly, it gets a little tiresome. Personally I've settled on TypeScript, which has nice IDE integration in VS Code so that it validates your types live while you code, while still giving you the flexibility of a dynamically typed language (it's all still JS at the end of the day).

Re:A Year in Clojure by ken on Sunday October 07, 2018 @10:32AM (#18160712)
> After writing the 10,000th unit test that only exists to ensure that you spelled some function/method/hash-key correctly, it gets a little tiresome.

One of the things I liked about Lisp when I first saw it was that, since it uses kebab-case and (usually) full words, I could just turn on my standard English spell-checker. After all, if "Programs must be written for people to read, and only incidentally for machines to execute", then it makes sense that I should be able to run a normal spell-checker on them. The only reason that was infeasible in languages like C is because they used (for both technical and cultural reasons) names like "memcpy".

Whatever field I'm working in already has its own vocabulary. I don't need to create a new <domain>-in-<proglang> vocabulary for a program about it. Or if I do invent an abbreviation that's so useful that I want to use it in my source code, I'll probably also want to use it in my documentation, webpage, emails, etc.

Most large programs that I've worked on, in other languages, have at least a couple misspellings in func/var names, which even Haskell's type system won't catch. "Referer" was clearly not written by someone with their spell-checker turned on for source code!

Re:A Year in Clojure by azureus on Sunday October 07, 2018 @10:04AM (#18160583)
Once a month, I find myself wishing Clojure had static typing, before dramatically rediscovering why not having it can be such a relief. The reasons for this are expressed much more articulately than I can here: https://lispcast.com/clojure-and-types/ ... Personally, for the kind of work that I do, I've found clojure's compromises make my life on net easier, and my code, on net, more reasonable.
Re:A Year in Clojure by bunderbunder on Sunday October 07, 2018 @10:48AM (#18160806)
I'm finding that it really depends heavily on the domain I'm working in. Roughly speaking, if I'm dealing with "business problems" type stuff - grab data, report on it, keep a paper trail, etc. - I want a very loose and flexible programming environment with a REPL and dynamic typing and all that. Python, for example, treats me very well when I'm wearing my "data scientist" hat.

OTOH, when I'm doing more "building infrastructure" type work - implementing a data store, writing a compiler or interpreter, stuff like that - I start getting more interested in rigidity and formality. Static languages treat me well in these situations. The more rigid, the better - I'll prefer Scala to Java, for example, specifically because it gives me more tools in the type safety department.

Re:A Year in Clojure by mclehman on Sunday October 07, 2018 @02:20PM (#18161900)
I pretty much agree with this. When I can get away with it performance-wise (and I almost always can for personal projects), this is a big part of why I love using Perl 6.

I recently rolled my own (verg tiny and specific) build/deployment system for a project. Initial prototyping was a breeze, but once I had things more or less nailed down, I started adding types and, for example, was able to leverage the type system to make sure it can only be deployed listening on IP addresses in the private range. The next step is to rig up some sort of Zerotier integration to ensure I only make it available on my private networks.

Re:A Year in Clojure by azureus on Sunday October 07, 2018 @04:46PM (#18162523)
Yes, I wish I had clarified that I meant that Clojure beats everything else for me for what you describe as "business problems" and also, I've found it quite enjoyable for writing simple games. Im afraid im not well qualified to comment on what "infrastucture" type work would be like, and what is best for it.
Re:A Year in Clojure by threatofrain on Sunday October 07, 2018 @02:53PM (#18162060)
I think nowadays the comparison is vs gradual typing.
Re:A Year in Clojure by gered on Sunday October 07, 2018 @10:56AM (#18160853)
I can't say I've personally seen any evidence of "mass migrations" of this sort. However, I can personally say that after 5 years of working with Clojure and with several large Clojure projects under my belt now, I'm starting to realize that I'm losing confidence in using Clojure for large projects and the feeling of how _maintainable_ that project will be in 2-3 years or longer. I think Clojure hits a sweet spot (at least for _me_ personally) for small-to-medium sized projects though, and I don't hesitate to use it in those cases.

The thing I've _consistently_ seen with every large Clojure project I've been a part of is that as the project grows, it becomes harder to make sense of the types used across the project. On the last couple of these projects I've been on, Schema was used to annotate many types, but I've never found this to be quite enough and I still remain firmly convinced that gradual typing just doesn't work well enough generally. I think that with a _very_ highly disciplined team of experienced developers it could probably work well. But we don't all get the opportunity to work on such teams. What I've personally seen is developers are all too willing to either add a partial type spec/annotation (with probably some s/Any's lazily thrown in where there should most definitely _not_ be), or just flat out not adding any at all ("Why would I need it here? This is very simple code and it's obvious what is happening here!" ... sure, maybe it is to you now... what about in 6 months? Or what about to one of the other developers on the team?).

When discussions about static vs dynamic typing come up (which is almost always a fruitless argument in my experience), I've noticed that the dynamic typing proponents tend to get overly focused on the idea of "correctness". This has always disappointed me. Correctness is certainly an important benefit (though I think it tends to get exaggerated a bit much), however, to me the idea of documentation in the form of a type definition is _much_ more valuable over the longer term, especially so for large projects. Developers are usually quite lazy, especially when it comes to documentation, and often when looking at a piece of code in a large code base, the only documentation about the values being passed to a function is going to be the type definitions (aside from reading the code itself of course).

Re:A Year in Clojure by sheepmullet on Sunday October 07, 2018 @08:02PM (#18163296)
> using Clojure for large projects and the feeling of how _maintainable_ that project will be in 2-3 years or longer

Maintainable in what sense? And large in what sense?

I typically work on 1-10 million lines of Java code solutions split up into maybe 20-50 seperate components.

These 20-50 seperate components have untyped boundaries between them.

Setup correctly these untyped application boundaries rarely cause problems - of course you have to put a bit more thought into architecture, documentation, and verification but it’s not particularly onerous and we have been doing it successfully for decades now.

I’d say anecdotally errors caused by this lack of typing are well under 1% of total errors.

Re:A Year in Clojure by dorfsmay on Monday October 08, 2018 @12:33AM (#18164159)
> every large Clojure project I've been a part of is that as the project grows, it becomes harder to make sense of the types used across the project.

Are there languages you've seen that don't show an ugly side as projects grow to a large size?

Re:A Year in Clojure by thoughtfunction on Sunday October 07, 2018 @02:59PM (#18162084)
You see these migrations as companies grow past certain engineer counts if they start out with dynamic languages. Sometimes they might even modify the language itself to bolt on types on it.

Ex: Facebook and adding types with Hack & Flow. Dropbox's work to add types to python. Uber used to be a python and nodejs shop, and now new stuff is done in golang and java. Many nodejs shops go from pure js to typescript or flow.

Gradual typing is useful for companies that don't want to immediately migrate everything at once. If you have the right policy of 'type everything you touch', enforced with a linter, a codebase gets migrated relatively quickly.

Re:A Year in Clojure by yogthos on Sunday October 07, 2018 @03:27PM (#18162209)
My question is why anybody thinks that writing a project in a monolithic style is a good idea. Any large project can be broken down into small independent modules, and there are tons of benefits to doing that aside from being able to track types in it.

Independent modules are easier to reason about, they're reusable, and they can be maintained on independent schedules. The biggest advantage though is that this approach allows you to split teams up into smaller groups responsible for the individual modules. One of the biggest problems in maintaining large projects is communication overhead, and typically it's very difficult for teams with over 5-6 people to be productive.

So, if your project outgrows limits of dynamic typing, chances are it should be split up instead of using static typing as a crutch.

Re:A Year in Clojure by StreamBright on Sunday October 07, 2018 @11:40AM (#18161085)
Every programming environment has down sides and Clojure is no exception but I still think that using Java instead of Clojure is not better because simply you have 3-10x source code to maintain and the number of bugs are correlating with number of source code lines. On the top, I quite often see null pointer exceptions in Java when in theory that should never happen because we are statically typed.
Re:A Year in Clojure by hota_mazi on Sunday October 07, 2018 @12:29PM (#18161352)
Pick a more modern language than 20 year old Java, such as Kotlin, and you get the best of both worlds: static typing and type inference.
Re:A Year in Clojure by StreamBright on Sunday October 07, 2018 @12:56PM (#18161516)
Yeah it is on my list to check for a while. If it has the same interop with Java as CLojure than I will give it a try.
Re:A Year in Clojure by gered on Sunday October 07, 2018 @11:59AM (#18161185)
I'm not sure why you're contrasting Clojure and Java, but nowhere was I recommending using Java over Clojure (nor would I ever offer such a recommendation). Certainly there are much, much better choices for a statically typed language out there.
Re:A Year in Clojure by StreamBright on Sunday October 07, 2018 @01:00PM (#18161533)
People usually not just chose languages. The VM, libraries and dependency management all together are chose. Btw. this is why Rich started Clojure on the JVM. If you stay on JVM land I quite often hear that the developers do not want to try Clojure because of the lack of types (which refers to static typing in this context). Good luck implementing a Hadoop project in OCaml or Haskell.
Re:A Year in Clojure by platz on Sunday October 07, 2018 @01:38PM (#18161715)
It seems like you're jumping ahead to conclusions waay to quickly here.

Instead of that, it would be better to respond directly to the specific points the OP made, rather than move the argument to a bunch of new topics.

OP did not say anything about other languages, be it Java or otherwise. OP is just describing experience with closure.

In order to understand clojure better, it would be good to not shut out feedback.

Re:A Year in Clojure by StreamBright on Sunday October 07, 2018 @06:02PM (#18162862)
This is exactly the point, you can't just chose languages because of type systems there is a lot more to these decisions. OP has an experience with Clojure (which has closures) but these experiences are do not exists in the vacuum, there are tradeoffs that you take with every language.
Re:A Year in Clojure by logicchains on Sunday October 07, 2018 @11:57AM (#18161174)
>On the top, I quite often see null pointer exceptions in Java when in theory that should never happen because we are statically typed.

In this sense Java is actually dynamically typed: there's no way of knowing at compile time whether an object is actually an instance of that object or is null. In languages like Kotlin, Haskell and Ocaml, which specify in the type system whether or not something can be none and force the user to check before using something that can be null, null pointer errors don't happen.

Re:A Year in Clojure by lvh on Sunday October 07, 2018 @12:25PM (#18161328)
> In this sense Java is actually dynamically typed: there's no way of knowing at compile time whether an object is actually an instance of that object or is null.

That is an extremely unusual definition of dynamic typing. If I have an Integer it might be null but it won’t be a BeanFactory (unless someone did a reflective call or wrote some bytecode or otherwise subverted the type checker).

Re:A Year in Clojure by StreamBright on Sunday October 07, 2018 @01:01PM (#18161541)
Well in my ideal word nulls are not part of Integers or Floats or BeanFactory.
Re:A Year in Clojure by thoughtfunction on Sunday October 07, 2018 @03:06PM (#18162114)
Static type systems have scopes in how much they type. Some newer ones add nullable annotations, like kotlin or swift, others don't have it yet.

I hope java gets native support for @nullable, but for now we have stuff like error prone and @nullable annotations.

Re:A Year in Clojure by dustingetz on Sunday October 07, 2018 @11:17AM (#18160953)
Clojure goes to great lengths in the design-space to craft the puzzle pieces – immutability, lambdas, simplicity, blessed primitives for state and identity – in such a way that Clojure's idioms are centered on clarity of thought instead of guard rails. This is why Clojure works (for people willing to go deep) in many places where Js/Ruby/Python don't.
Re:A Year in Clojure by rasteau on Sunday October 07, 2018 @10:38AM (#18160750)
> finding the "joy" of dynamically typed languages and all the "freedom" they give you ... verifying those types at compile-time was actually pretty useful

Indeed one of the "joys" is that one can apply that verification a la carte, and possibly with different approaches [0][1]. Similarly, one may desire to dispatch on type [2], but might sometimes want arbitrary dispatch [3].

> After writing the 10,000th unit test that only exists to ensure that you spelled some function/method/hash-key correctly

This too is solvable with a la carte tooling [4].

Everything has trade-offs. Static type systems give a fixed set of benefits with a fixed set of costs. Some folks prefer to use their experience and judgement to choose when, where, and how to pay the associated costs (and that freedom of choice itself has a cost). It's okay to have different preferences.

[0] https://github.com/plumatic/schema

[1] https://clojure.org/guides/spec#_entity_maps

[2] https://clojure.org/reference/protocols

[3] https://clojure.org/reference/multimethods

[4] https://clojure.org/guides/spec#_testing

Re:A Year in Clojure by lvh on Sunday October 07, 2018 @11:08AM (#18160912)
Function specs are not about compile time verification: they are about checking that the object you got is the thing you thought you had.

Compile time verification is nice if you know all your inputs at compile time. At some point, data from elsewhere comes in. Specs are an answer that preserve the composability goals of Clojure (who cares if I have a bit more information than I strictly need?) and allow you to validate them at runtime with useful error messages.

spec has incidentally made Clojure's compile term error messages better! But that's just because one program's compile time is the compiler's runtime, and the compiler is running spec verifications on your code (which is after all, just data!) during its runtime :-)

Re:A Year in Clojure by mhluongo on Sunday October 07, 2018 @09:50AM (#18160520)
Would love to see some evidence of a mass migration to Haskell... anywhere.
Re:A Year in Clojure by CodeArtisan on Sunday October 07, 2018 @10:31AM (#18160710)
Would not call that a massive migration but

https://code.facebook.com/security/fighting-spam-with-haskel...

Re:A Year in Clojure by specialp on Sunday October 07, 2018 @10:48AM (#18160805)
This is a problem that is very well suited to Haskell, but it is a subset of general software development. Here it is worth the substantial overhead of making robust types as independent teams are making rules for this spam filter and typing can enforce order on it as they are probably easily categorized by type of filter. There are also quant developers that enjoy writing in OCaml, and AI devs too. But besides major companies using statically typed languages for some special teams, we don't see companies switching wholesale. Like I am still waiting for the day where $bigCompany says "we have hit so many type problems in our code that we have decided switching to staticaly typed languages will save us tons of money and are now requiring it".
Re:A Year in Clojure by platz on Sunday October 07, 2018 @12:30PM (#18161359)
It is really not in a companies interest to ever do a 'mass migration' excepting dire or takeover situatuons
Re:A Year in Clojure by mhluongo on Sunday October 07, 2018 @02:12PM (#18161867)
I meant in the industry (as I believe the original comment intended), not one large enterprise migrating.
Re:A Year in Clojure by platz on Sunday October 07, 2018 @02:21PM (#18161903)
Well, in industry.. you know that clojure and haskell folks have more in common than different. A lot of the same folks move back and forth and care about the same things, just different ways to get there. I think the community size of clojure and haskell are sized very similarly and are also highly correlated. Any language graph you see that has both clojure and haskell, they always appear very close to each other.

Lest you forget, both fight against Java: https://trends.google.com/trends/explore?date=today%205-y&ge...

Industry is not doing a 'mass migration' to clojure, haskell, or any other language not in the top 5.

So the OP was wrong to claim a 'mass migration' in the first place

Re:A Year in Clojure by moocowtruck on Sunday October 07, 2018 @10:57AM (#18160855)
there is no such thing so don't get your expectations too high
Re:A Year in Clojure by juskrey on Sunday October 07, 2018 @10:17AM (#18160632)
Specs in Clojure are not static typing. Satisfying spec does not mean a value contains only items described in spec.

It is an implementation of much larger messaging idea - message receiver only takes care about data it needs and nothing else.

Re:A Year in Clojure by specialp on Sunday October 07, 2018 @11:03AM (#18160893)
Rich Hickey said himself "If a delivery truck comes to deliver a TV to you, do you care what else is on the truck? No, you are only interested in your TV" so the messaging idea is exactly right, and they weren't looking to implement strict static typing. In Clojure, Rich Hickey and others like Stuart Holloway speak of not "complecting" your code by passing around data structures other than uniquely namespaced maps where functions can take what they need and operate on it without having to mess with anything but data.

so instead of

      data Person = Person String String Int Float String String 

     you have :person/name ....
By using simple data structures you can work on it much easier and even interoperate with other services and languages, while still checking to see if the data you are fed conforms to what you need.
Re:A Year in Clojure by wild_preference on Sunday October 07, 2018 @11:49AM (#18161140)
You can get that with ad hoc structural typing or by transforming into canonical nominal types that contain the subset of data you care about.

I’m not sure what these comments are arguing against, but it’s not compile-time analysis.

Re:A Year in Clojure by lvh on Sunday October 07, 2018 @12:05PM (#18161212)
Thank $DEITY that impracticality is always because you’re not using the One True Static Typing School!

Seriously: you’re describing a feature that only exists in a small subset of languages and only a small subset of contexts at that. OCaml and Go do this but not for data, just for methods.

Re:A Year in Clojure by wild_preference on Sunday October 07, 2018 @12:15PM (#18161271)
Absolutely. I wish I could structurally type a db query function in every statically typed language that doesn’t have it. Instead I end up with the challenge of coming up with the smallest number of (what is essentially) User1, User2, etc. and hope my queries and subqueries can always fill one of the existing shapes which is a significant pain point.

The language used in this thread so far has made it seem like runtime specs are the solution to this “only care about part of the bag” problem rather than what I consider a general feature of dynamic typing that certainly can be expressed statically as well.

Re:A Year in Clojure by lvh on Sunday October 07, 2018 @12:18PM (#18161292)
I can’t parse that second paragraph. Can you elaborate?

Runtime specs absolutely also validate. And you can run them at test time. And you can put arbitrary code in them if you want. And sometimes you can statically validate them.

What I’m saying is you get ad hoc structural typing on everything, including data, which you’ve already said is a good thing, so I’m not sure what we’re arguing about or if we’re even arguing :)

EDIT: ah! You edited the paragraph. Simple question: where does this ideal static typing thing ship? Like, how do I do this on my machine right now? What package do I install?

Re:A Year in Clojure by wild_preference on Sunday October 07, 2018 @01:20PM (#18161621)
If I could go back, I would’ve rephrased my original post so that it didn’t seem like it existed just to contradict the upstream posts.

People were talking about using runtime specs to annotate and validate the subset of information a function may care about. Which is certainly something I wish I could do in more languages, though ideally as much at compile time as possible.

Of course, runtime specs in Clojure let you make more assertions than just structure, so structural typing as seen in TypeScript, for example, certainly don’t go all the way. It just helps address what I thought was being discussed.

What is the ideal middleground? I think one possibility would be if Clojure was statically typed to begin with yet had the syntax of spec/schema.

Though I’m not sure we’re talking about the same thing anymore.

Re:A Year in Clojure by lvh on Sunday October 07, 2018 @05:52PM (#18162822)
We're not. But: good news: people are experimenting with verifying specs at compile time! And since specs come with quickcheck built in, you get a lot of "free" (in developer hours) "compile time" (actually: development time) verification. I appreciate that is not the same thing as a proof system :-)
Re:A Year in Clojure by rafd on Sunday October 07, 2018 @10:25AM (#18160678)
You can use spec to verify that "a value only contains items described in the spec". The core library doesn't make it easy, but spell-spec [1] contains some useful helpers.

[1] https://github.com/bhauman/spell-spec

Re:A Year in Clojure by typon on Sunday October 07, 2018 @11:01AM (#18160876)
Almost every week a new article comes out from some language index saying Python is at the top or overtaking Java or C or C++ to reach the top very soon. I think the evidence is simply not there to support that static typing is somehow preferred by the average programmer.
Re:A Year in Clojure by hota_mazi on Sunday October 07, 2018 @12:26PM (#18161336)
There is plenty of evidence showing that the trend is toward static typing, not dynamic typing.

Starting with the fact that most dynamically typed languages today are progressively adding type checking (spec in Clojure, gradual typing in Groovy, etc...) while no statically typed languages are going the opposite way.

Re:A Year in Clojure by dragonwriter on Sunday October 07, 2018 @07:39PM (#18163218)
> Starting with the fact that most dynamically typed languages today are progressively adding type checking

At the same time, industrially-popular static languages have added dynamic escape hatches. The trend is really toward optional static type checking, with dynamic languages moving to opt-in typechecking and static languages moving to opt-out.

Re:A Year in Clojure by zmmmmm on Sunday October 07, 2018 @09:06PM (#18163498)
I agree, and interestingly I see it in my own Groovy code evolving over time. I look back at old code and it is full of needlessly dynamic types. Then I look at what I write now and I have just gravitated towards typing everything more and more. It's still great to be able to fall back to dynamic typing within local context where there is no loss of clarity from doing so. But putting a "hard shell" around the outside - function definitions, variable declarations, data types etc. makes me feel so much better about the maintainability and interpretability of the code. Sometimes I think I should just use Kotlin, but then I would miss the ability to drop seamlessly into dynamic mode when that makes sense. Especially in the REPL it is super nice.
Re:A Year in Clojure by vorg on Monday October 08, 2018 @05:18AM (#18165670)
> Sometimes I think I should just use Kotlin, but then I would miss the ability to drop seamlessly into dynamic mode

You can use Apache Groovy for just the glue code and Gradle build scripts, and use a statically-typed language like Java, Kotlin, or Scala for the actual system you're building -- that's what most programmers do.

Re:A Year in Clojure by vorg on Sunday October 07, 2018 @07:22PM (#18163167)
When statically typed language C# added a dynamic keyword, that's a language going the other way.

Of course, no-one actually thinks of C# as being a dynamically typed language despite its dynamic keyword, nor does anyone think of Apache Groovy as being a statically typed language despite its @CompileStatic annotation. In the everyday world, C# is used for building performant systems and Groovy is used for glue code and build scripts.

Languages tend to go both ways nowadays. Go first shipped with both static typing (though without generics except for builtin datatypes) and dynamic typing (though using the wordy interface{} marker).

Re:A Year in Clojure by sheepmullet on Sunday October 07, 2018 @07:36PM (#18163207)
> while no statically typed languages are going the opposite way.

Static languages added dynamic capabilities with reflection and run time code generation a long time ago.

Re:A Year in Clojure by bunderbunder on Sunday October 07, 2018 @11:33AM (#18161046)
I think there's a very good chance that this is being driven by the flap around data science, and the big push toward teaching programming in schools.

Assuming, for the sake of argument, that that's true, it means that we've got to think about how you define "programmer". Does it mean anyone who uses a programming language, or does it mean something more like "software developer"?

TIOBE, for example, effectively measures popularity among the former group, not the latter. Which means that, in the case of Python popularity, it's going to be picking up on all sorts of things: analysts and BI people ditching Excel in favor of Pandas, ops people ditching piles of shell scripts in favor of Ansible, graduate students choosing Scipy over R or Matlab, 7th graders with Pi-tops, ...

Re:A Year in Clojure by truculent on Sunday October 07, 2018 @10:29AM (#18160701)
I had a similar experience when I spent some time playing with Elm. Being able to (re)model the structure of your program and have the compiler verify it is absolutely freeing.
Re:A Year in Clojure by yogthos on Sunday October 07, 2018 @03:17PM (#18162167)
That is pure and utter FUD I'm afraid. My team has been working with Clojure for over 8 years now. We have tons of code in production, and we don't have any problems maintaining these projects. Furthermore, there's absolutely no empirical evidence to support the notion that static typing results in statistically significant reduction in defects, speed of development, or ease of maintenance. Considering how long both disciplines have been around, and the sheer amount of software written in each, that's quite the elephant in the room. I also have no idea who the people migrating from Clojure to OCaml, Haskell, or F# are. I don't think I've seen a single story about a company doing that.

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. The other problem is that OO encourages proliferation of types in your code. 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.

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](http://blog.jayfields.com/2014/01/repl-driven-development.ht...) 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 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.

Re:A Year in Clojure by dwohnitmok on Sunday October 07, 2018 @08:23PM (#18163356)
Our team (not at liberty to say which company unfortunately) moved away from Clojure back to Scala, or more accurately we built a couple of production services in Clojure, sunsetted them and haven't used Clojure since; the most vocal Clojure enthusiast moved on to another project and the rest of us were kind of meh on using it in the future after about a year of experience and went back to Scala (although certain things were quite fun and it was a fantastic learning experience and I would highly recommend at least getting familiar with REPL-based programming).

It wasn't horrendous (and it's not like Scala is absolutely amazing for us), though maintenance was something of a pain point. We might use Clojure again, especially with some new blood who are Clojure enthusiasts.

These days I'm less and less certain of what proactive role a language plays in team success. That is to say I think the arrow of causation is usually pointed in the wrong direction. A well-functioning, excellent team will, given sufficient latitude, converge on a language that suits a team well. This language is often different for each team. Introducing a "good" language is unlikely to save a team.

Re:A Year in Clojure by yogthos on Sunday October 07, 2018 @08:55PM (#18163468)
I'm in complete agreement here. Ultimately you want to use the language that the team enjoys working in. Different people have differing experience, aesthetics, and pain points. You want to pick the language that most devs on the team prefer using.
Re:A Year in Clojure by taylorwood on Sunday October 07, 2018 @04:10PM (#18162385)
>What I find to be of highest importance is the ability to reason about parts of the application in isolation

This is a huge factor I'd like to emphasize and write about in a follow up post, having worked on many Clojure projects at different companies now... Another Year in Clojure :)

I've maintained some large Clojure codebases I didn't write, and the easy ones shared this quality. The more painful ones were reminiscent of large OOP codebases and honestly I would've preferred they'd been written in Java.

Re:A Year in Clojure by platz on Sunday October 07, 2018 @04:03PM (#18162359)
OP was mistaken to claim 'mass migrations' of any kind - that was clearly made up.

alternatively, I'm always receptive to reports about experiences.

I'm very skeptical about demands to produce "empirical evidence" for or against any sociological programming practice, because it tends to be a method to place the burden of proof on the opposing side, while reserving the default position for own own side - which just turns out to be a way to normalize one's own beliefs.

Re:A Year in Clojure by yogthos on Sunday October 07, 2018 @04:43PM (#18162508)
The only reason I talk about empirical evidence is because I constantly see sweeping claims regarding efficacy of static typing. If somebody makes broad claims of one approach being strictly superior to the other, then the burden of proof is indeed on them to support said claims. The null hypothesis is that both approaches are effective, and it's in line with all the currently available research on the topic [1].

Furthermore, fixating on static typing as the one defining feature of a language amounts to concept space reduction. In practice, there are many complex factors that contribute to overall quality of the language that have to be considered holistically.

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

Re:A Year in Clojure by uxcolumbo on Sunday October 07, 2018 @05:11PM (#18162638)
> I can even connect to the REPL in production

Programming is not my day job, so please correct me if I'm wrong - but every project I was involved in so far - having access to a production DB is a big no no.

Or does the project you refered to not contain any sensitive data, so having direct access to the production system is deemed 'safe'?

Re:A Year in Clojure by Jach on Sunday October 07, 2018 @11:35PM (#18163962)
Different companies do it differently, but I'm not aware of any that make it absolutely impossible to get at production data (that hasn't been encrypted with a key they don't have) and program instances, just some make one (reasonably) jump through many more hoops than others. It's very much a double-edged sword so access control, auditing, etc. needs to be done carefully. (Privacy By Design.) The value can be immense -- e.g. debugging and hotloading a patch on a spacecraft 100 million miles away (http://www.flownet.com/gat/jpl-lisp.html)
Re:A Year in Clojure by yogthos on Sunday October 07, 2018 @08:59PM (#18163480)
My team both develops and supports the applications that we build in production. When issues are reported, it's often necessary to check the database to resolve it. Being able to check what data the application is pulling during requests is very useful for troubleshooting.
Re:A Year in Clojure by physicsyogi on Sunday October 07, 2018 @04:37PM (#18162490)
The only example of a Clojure team migrating to Haskell is the Duckling team, a little while after Facebook acquired them. Among other things, they wanted a language with better support within Facebook.

https://medium.com/wit-ai/open-sourcing-our-new-duckling-47f...

Re:A Year in Clojure by yogthos on Sunday October 07, 2018 @04:49PM (#18162538)
Incidentally, that's a perfectly valid reason to migrate, but it's not the one the parent suggests. :)
Re:A Year in Clojure by jb1991 on Sunday October 07, 2018 @05:57PM (#18162844)
> The other problem is that OO encourages proliferation of types in your code. Keeping track of that quickly gets out of hand.

I'm curious how Clojure avoids this. In my large Clojure projects, there aren't "types" in the sense of static typing, but the shapes of data still exist, the types are there, but they are in my head or in other forms than that which a compiler can check. I still have to remember that a map is a certain "type" of data with certain keys/values that are also of certain types, and a large project might have many of these "types", even if they are not explicit. That is equally hard to maintain for me -- harder, really, since it's too easy to make mistakes.

Re:A Year in Clojure by yogthos on Sunday October 07, 2018 @06:29PM (#18162961)
The main difference I find is that with Clojure approach you have a common set of data structures that you operate on using common functions from the standard library. I find that it's usually easy to tell what type of data you're working with by seeing how these functions are used. When I see functions like map, filter, iterate, and so on, I have context for what the intent of the code is, and this helps me know what type of data I'm working with. At the same time I'm mostly working in a local context thanks to immutability, so I don't have to worry that a piece of data might get changed from under you via a side effect.

I also find that you learn patterns for structuring code that make it easier to track the types. For example, I avoid renaming keys or restructuring data except at the edges of the application. Adding Spec or Schema at component boundaries is also helpful, as you can see what the shape of the data was at the API level and work your way through the helpers from there.

There's also a big benefit of working with data as opposed to objects. Each class is its own ad hoc DSL with custom behaviors. Knowing what one class does tells you nothing about what another does. This approach doesn't scale well for large applications where you have thousands of classes with each with its own behaviors.

Re:A Year in Clojure by jb1991 on Sunday October 07, 2018 @06:38PM (#18162998)
I think I would definitely benefit from blog posts or whole books on Clojure style specifically, rather than the many constant re-introductions to its syntax and data structures (like the one in this article). Your thoughts have come closer to explaining a maintainable approach to the language than most of what is out there. In my experience, despite working for years professionally with Clojure, I've never successfully felt like I wasn't fighting the language because of the constant and unexpected nils and other odd wranglings of data that were not intended and not easy to track down at runtime. The flexibility of the dynamic typing and the many APIs for manipulating data can make it easy to restructure your values in unintended ways, and this to me is a meaningful problem unique to the language. I don't have the problem in a Java app, for example, of a Foo type accidentally becoming a Bar type or even some other "type" that was never defined but now has a unique data shape because I made a typo somewhere or used the wrong library function, etc.
Re:A Year in Clojure by Scarbutt on Sunday October 07, 2018 @08:09PM (#18163314)
Regarding nil, its considered bad practice to use it as values in maps.
Re:A Year in Clojure by hellofunk on Sunday October 07, 2018 @08:20PM (#18163347)
Right, which is why I said “unexpected nils”.
Re:A Year in Clojure by ff_ on Sunday October 07, 2018 @10:25AM (#18160675)
This, so much. After some 3y spend doing Clojure, having a Haskell codebase is a totally new level of stress reduction.

All of a sudden you can stop worrying about "what am I getting in this function" and actually concentrate on the business logic.

(No, tests can't cover everything. Yes, we still have to write tests, but now they are all useful. Yes, the thing that types hinder you from doing changes is a lie, it's quite the opposite, much easier to change things since the compiler covers your ass)

Re:A Year in Clojure by yawn on Sunday October 07, 2018 @10:57AM (#18160858)
> All of a sudden you can stop worrying about "what am I getting in this function"

I think what you're saying is something like "this function guarantees that the thing I get here conforms to this shape" and I do like that. Something I don't see a lot of people express is just the simple "I know what the shape of this thing is by looking at the signature". Whenever I find myself reading other people's Clojure code, it feels like I end up spending more time figuring out what is being passed into a function. I have to look around to find the origin of a value. I wanted to tear my hair out the first time I tried reading Ring's source code to figure out how it worked. With F#'s Suave, I can tell what each function gets and what can be done to it--it's self documenting. In Clojure, as the original author you can play around in the repl full steam ahead because the types are in your head at the moment. Those who come after you have to assemble the puzzle with less information.

Re:A Year in Clojure by gered on Sunday October 07, 2018 @11:11AM (#18160931)
I think you've really summed it up well here. I have exactly the same concerns when looking at large Clojure codebases. It gets incredibly tiresome having to do this and even at the end of it, I'm still left with that nagging doubt "did I miss some detail somewhere?". Hopefully there's a unit test or three to help put my mind at ease but, at least at my work, that's really quite the pipe dream.
Re:A Year in Clojure by zmmmmm on Sunday October 07, 2018 @08:51PM (#18163455)
Couldn't agree more, but what I don't understand is that Python is plagued by this problem and nobody seems to have any issue with it. Trying to understand large Python codebases drives me crazy.
Re:A Year in Clojure by camgunz on Monday October 08, 2018 @12:33AM (#18164158)
I think a lot of Python engineers don't yet realize they have this problem. It's a classic issue in predominantly OO languages because you often get so caught up in defining objects you lose sight of control flow and data flow. Toss multiple inheritance and metaprogramming on top and it's basically hopeless.
Re:A Year in Clojure by StreamBright on Sunday October 07, 2018 @11:34AM (#18161054)
I usually put these into the description so the next engineer has a better time tracking what is what.
Re:A Year in Clojure by StreamBright on Sunday October 07, 2018 @11:33AM (#18161041)
It is funny, I usually know what I am getting in this function in my Clojure code. OCaml (or Haskell) gives entirely different comfort to me, it tells me what makes sense as input and output type for my function. The downside is that I need to define different functions for different types.

utop # let avg a b = (a +. b) /. 2.0;;

val avg : float -> float -> float = <fun>

utop # avg 2 4;;

Error: This expression has type int but an expression was expected of type float

utop # avg 2.0 4.0;;

- : float = 3.

user=> (defn avg [a b] (/ (+ a b) 2))

#'user/avg

user=> (avg 2 4) 3

user=> (avg 2.0 4.0) 3.0

I think it is a matter of taste which one you prefer. With property based testing and specs you can go very far in term of pre-runtime checks and there was some data about the number of bugs per line published by Github where it turned out that Clojure did not do too bad in comparison to statically typed languages.

Re:A Year in Clojure by ff_ on Sunday October 07, 2018 @11:43AM (#18161105)
I don't know about OCaml, but Haskell has typeclasses (which look like "polymorphic interfaces"?) so you can define functions generically and put a typeclass constraint to make them work on a certain class of types. E.g. your avg function would be of type:

avg :: Num a => a -> a -> a

Where your "a" is a number (that is, a type that implements the Num typeclass)

This would work out of the box on floats, ints, etc

Re:A Year in Clojure by StreamBright on Sunday October 07, 2018 @01:03PM (#18161552)
The signature is not really helpful. Can you show me how do you implement the actual function?

If you have operators that are typed like + and +. how can you make this work with Num?

Re:A Year in Clojure by ff_ on Sunday October 07, 2018 @01:16PM (#18161603)
Also the + operator is typed with Num; from a Haskell repl:

  λ> :t (+)
  (+) :: Num a => a -> a -> a
In case of average we need /:

  λ> :t (/)
  (/) :: Fractional a => a -> a -> a
So our type is going to be constrained by Fractional instead of Num). So an implementation could be (from here [1]):

  import Data.List
  average :: (Real a, Fractional b) => [a] -> b
  average xs = realToFrac (sum xs) / genericLength xs
[1]: https://stackoverflow.com/questions/2376981
Re:A Year in Clojure by boubiyeah on Sunday October 07, 2018 @09:33AM (#18160454)
Really like typescript. You could code an algorithm with very dynamic types then expose its API with very rigorous typing (TS's type system can go pretty advanced)
Re:A Year in Clojure by specialp on Sunday October 07, 2018 @10:40AM (#18160760)
I think the happy medium is a dynamically typed language that gives you the option to type or at least have a way to check typing. Java isn't really statically typed at all since it is necessary to cast in daily usage. Truly statically typed languages seem really cool but you don't see a lot of major things being done with them due to the overhead.

I have found too that when you are programming functionally as in Clojure you don't get bit by shared state things not being what you expected as everything you are writing should be data in, data out and easily reasoned upon. Nil handling in Ruby is a huge problem as people are chaining methods on mutable objects which becomes complicated, and this is the source of most bugs in Ruby. This is somewhat mitigated lately with safe navigation operator.

That is why TypeScript is appealing as you can use typing when you'd like to but still take in "unsafe" JavaScript or write in JS. Unlike if you were using Elm and had to write a wrapper to conform all JS code coming in.

Spec in Clojure is very nice as you can enforce and conform types where you need it, and also make generative testing for free. If there is some mass migration to OCaml, F# and Haskell I have yet to see it because very few businesses use them and the communities are much smaller.

Re:A Year in Clojure by moocowtruck on Sunday October 07, 2018 @10:56AM (#18160854)
does typescript allow you to write less tests? does it have dependent typing abilities? or something like clojure spec? to me it looks more like a slightly better java but you still need to write lots of tests
Re:A Year in Clojure by cube2222 on Sunday October 07, 2018 @10:18AM (#18160643)
Does anybody know, or (better) have any source on, how common typescript is on the backend?