Hacker News new | ask | show | jobs
by michaelochurch 4534 days ago
I'm familiar with type theory and (often) a proponent of dynamic typing.

It depends on what you're doing. If you're building cathedrals-- high-quality, performance-critical software that can never fail-- then static typing is a great tool, because it can do things that are very hard to do with unit testing, and you only pay the costs once in compilation. There are plenty of use cases in which I'd want to be using a statically typed language like OCaml (or, possibly, Rust).

If you're out in the bazaar-- say, building a web app that will have to contend with constant API changes and shifting needs, or building distributed systems designed to last decades without total failure (that may, like the Ship of Theseus, have all parts replaced) despite constant environmental change-- then dynamic typing often wins.

What I like about Clojure is that, being such a powerful language, you can get contracts and types and schemas but aren't bound to them. I like static typing in many ways, but Scala left me asking the question, any time someone insists that static typing is necessary: which static type system?

3 comments

I've heard this argument a lot, and I disagree. If your software is changing a lot, that is where types really shine. Refactoring is a breeze when you have types: you just change the code you want to improve, and all use sites are pointed to by the compiler. This is taken from daily experience: I work on a code base that is about 25K lines of Haskell and 35K lines of Javascript. Refactoring the Haskell is a pleasure. Refactoring the Javascript is something we dread, and always introduces bugs, some of which might linger for up to a year.
I've actually had this experience a couple of times on a project like upgrading a library from one release to another when APIs were updated. The compiler would often "show the way" by highlighting every mistake between version x and y.

I would go so far as to say one of the most evil things a person can do when designing an API for statically typed languages is using the equivalent of System.Object unnecessarily.

Exactly. A strong type system enables more rapid iteration than a weak one, just like writing unit tests. I was a fan of dynamic languages until I used Haskell and Scala, and saw how powerful types could be when they allowed you to express intention rather than getting in your way.

Now I relegate dynamic languages to writing single file scripts or less.

I think on top of this the self-documenting properties of a nice statically typed system push back the onset of code ossification quite a lot. I always trust my type documentation while I tend to be a skeptic of documentation that's more than a few months old unless it's being actively refactored regularly.
A strong type system can give you a lot of "free" documentation and I love it! I long for a search engine like Hoogle in other languages (http://www.haskell.org/hoogle/). Most functions are very obvious from their signature and name.

My favorite example are two functions with the signatures:

    fun1: a -> a
    fun2: Int -> Int
You might think the first is more powerful because it takes any type and returns that type, but ultimately, that function can only be the identify function (barring unusual things like exceptions, undefined values and runtime introspection). With the second function it could do all sorts of things. It might increment, it might decrement, it might divide by two and round down if the argument is a multiple of three.

I'll go further to say that it's very frustrating in languages like C++ and Java to have to deal with statics and objects. They have a lot of hidden state that isn't obvious from the type signature of their methods. Particularly the fact that some methods must be called before others—e.g. initializers—can make the behavior and side-effects non-obvious.

I agree. I define empty interfaces and the types to go with them for prototyping, and it makes building my prototype so much quicker. And with a better final product, too. So IMO static (or optional/gradual) typing can even be better in that way, but only if you think in types rather than hacking away til it works.
Hum, no.

I use Clojure at work and the single biggest drain on my productivity is a lack of a sensible, static type system. Yearn for Haskell big time.

I'd use Rust if I had to really get down and dirty, otherwise Haskell would be my first choice. OCaml isn't that great at all in practice.

Have you looked into Prismatic's Schema?

I fully agree that if you're working on a large project, you'll need something that is "type-like" in terms of enforcing interface integrity. Where there's an open question is how necessary compile-time typing is for most problems. (Few would disagree with the claim that types are a good thing.)

Clojure (esp. without types) seems to favor smaller projects (libraries over frameworks) and modularity in the extreme, almost implicitly. When you get "into the large" and need checks like types or contracts, there are various libraries that are available.

I like Haskell a lot, but my experience going between the dynamic and static styles of programming and languages (these things are as much about coding style as the language itself, which is why Scala can be beautiful or horrid depending on how it is used) is that the grass often seems greener on the other side. Haskell's great in many ways, and designed by some of the most brilliant computer scientists and logicians alive right now, but it's not without some painful warts (although my knowledge is 3-4 years out of date).

Yeah, it's not interesting.

I use Clojure a lot at home and work. I'm an active participant in the community. I've also discussed core.typed with Ambrose to my satisfaction.

If you do Clojure web development, there odds aren't terrible you're using a library I've either made or worked on. (Korma, lib-noir, Selmer, luminus, Revise, bulwark, blackwater, trajectile, clj-time, brambling)

https://github.com/bitemyapp?tab=repositories

I'm tired of tracking down type errors in Clojure.

I'm tired of increased source->sink distances in runtime errors compared to compile-time errors.

I want to be able to refactor my code fearless, period, end of story with static assurances.

Record (product) types handle statically verifying schematic use of data. I'd rather have that work statically so that I can minimize source->sink distance. That obviates the need for a "schema" library.

I'm a very active Clojure user, I end up having to explain the same things over and over as to why I'm moving my stuff over to Haskell.

The most thorough way to go is to do what I did and just learn Haskell to decide for yourself. Don't try to paper over the problems with 1/4 solutions.

What you're saying makes a lot of sense. Clojure has a lot to recommend it (especially the JVM, in business) but there are a lot of benefits to Haskell.

The "source->sink" problem is a pain, I agree. It's one of those dangers of macros and metaprogramming that seems to be difficult to resolve. Generally, I only run into nastiness there, though, when I'm trying to do things that would be very hard to do in statically-typed languages.

What I've noticed in Scala and Ocaml is that people end up hacking the compiler (see Jane Street's "with sexp" and "with fields"). That, to me, has all the negatives that come from macros and dynamic typing. A compiler that does static typing is great, but if it ends up being hacked, then all bets are off and I'd rather use macros. (I'm playing Devil's Advocate here; I know that most web apps aren't going to require compiler hacks, but most companies, given enough time, will find reasons that they need to hack the compiler.)

I'm curious about your experiences with Haskell. What negatives have you found in the language? (I like it a lot, but haven't used it for anything big.) How strong is the story for the web? What are the build tools like; are they mature, or obviously in need of work (as in, say, Ocaml or Scala)?

>would be very hard to do in statically-typed languages.

I doubt these things apply to Haskell, which is part of my point. It offers a lot of rope for the self-hanging if you want, but the defaults (static, safe, strong, immutable, pure, lazy) are the best place to start.

There are a hundred things people think "only dynamic languages can do" that you can do in Haskell.

I have yet to get anybody to contrive something you can't do in Haskell that you can in other languages.

This is a mere glimpse, but should give you an idea:

http://hackage.haskell.org/package/base-4.6.0.1/docs/Data-Dy...

^^ Uni-typed languages are a subset of proper type systems. This is a practical application of that idea.

You don't generally hack GHC unless you're an enthusiast, although that option is certainly available to you and it's much easier than you'd expect.

Negatives? More people using it would improve library coverage, but the libraries are very good. The web story is better than Clojure. Scala web vs. Haskell web entirely and utterly depends on whether you're happy with Scala's Play Framework or assorted uber-micro-frameworks. If you're not and would prefer a more diverse and componentized ecosystem of libraries, Haskell's is better. You can go whole-hog with a single framework like Snap or Yesod, but their individual components are eminently reusable libraries. Yesod even checks the damn URLs in your templates at build-time.

The interactive workflow in GHCi is legitimately better than Clojure's. I'm not exaggerating. It's not as good as the glory days of Common Lisp + swank + Emacs, but it's better than Clojure + Emacs + nrepl.

Bonus? Has a fucking debugger - unlike Clojure.

Native binaries, multiple quality vector libraries that support CUDA. Burgeoning but respectable family of bioinformatics libraries.

Negatives? Forces you think and learn. There's a ramp-up time, but the other side of the first hump or two has some pretty serious leverage. Doesn't have the library diversity of Perl or Python, but the building blocks are incredible (Parsec, attoparsec, Aeson, etc).

Another negative? You'll have even less patience with poorly designed static languages.

It's worth noting that I'd sooner use Clojure than OCaml, but I'd sooner use Haskell than Clojure.

I don't fetishize type systems in general, I just really find Haskell very pleasing and productive.

The build tools are mature and quite nice now that Cabal has sandboxing built in by default. Put it this way, I don't cringe at all when I use cabal the way I did with Scala's sbt.

Another difference between Haskell and Scala is that it uses a relative conservative core augmented by potentially unsound/unsafe augmentations like GADTs. http://en.wikibooks.org/wiki/Haskell/GADT

What this helps with is it means you only have to think about the "core" of the language at any given time, but if somebody is using magic like Template Haskell (macros), you get that documentation in the form of a pragma at the top of the file. I quite like it.

The laziness is also important, it obviates the need of 80-90% of how macros get used in languages like Clojure. It makes functions themselves more general and useful.

Another negative? A sufficiently "specialized" application of Haskell might mean you're on the frontier. But the same is true of Clojure. I don't find that really matters nor do I find the situation truly improves by using something like Java or Ruby.

I'd rather just read a white paper, sip tea, think, and then sit down to write the code in a language that won't waste my time.

I would add that Haskell's sweet spots extend from Java -> Python -> Clojure. I wouldn't use it for systems programming - for that I'd use Rust.

I wish I could give this more than 1 upvote. Really good response.

My Haskell experience is a few years out of date (and not as deep as my OCaml knowledge) but it sounds like the language has advanced pretty far.

I'll still be using Clojure for the next 3-4 years (my company uses the JVM heavily, and Clojure still beats Scala IMO) but I'll have to look into the current state of things in Haskell.

Out of curiosity: what were your experiences with OCaml, and why didn't you like it?

Interesting insights. Would love to read more aobut this. Usually the arguments are "I'm a bad programmer like everyone else so I need verification" or "the world is dynamic".
The good arguments for static typing are much different than "I'm a bad programmer".

Google "Theorems for free". One of the most life-altering CS papers I've read. :-)