Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

In my experience, unless you enable all the crazy type system extensions (which isn't a terribly good idea IMO), GHC doesn't really spend that much time typechecking programs. Most of the time is spent on optimizations, which are (0) possible thanks to purity [hooray!], (1) necessary to overcome the limitations of laziness [dangit!].

In any case, Haskell supports exploratory programming just fine. It's true that Haskell's definition of "exploring" (refine your design until you can convice GHCi and QuickCheck that it makes sense) is somewhat idiosyncratic from a Lisper or Smalltalker's point of view (refine your design until you can convince yourself that it makes sense), but that doesn't make it any less legitimate. It's still experimenting, except now you have a tool to validate the degree to which the experiment has been a success. It's experimental science, not experimental art.

---

On the specific topic of nils, I think it's fair to say they're an absolute mistake in a statically typed language first released as late as in 2007. Standard ML and OCaml, both much older languages, have parametric polymorphism, algebraic data types, freedom from nils, and fast compilers: Poly/ML and OCaml's bytecode compiler, respectively. In addition, both Standad ML and OCaml have unrestricted imperative facilities, so freedom from nils is perfectly possible in an imperative language.

---

Regarding the classes of errors that are still possible in type systems like Haskell's, I have found only one that really matters, and where the usual workarounds (e.g., phantom types) don't help. Haskell, being a mathematically-inspired language, favors programming in terms of "timeless" entities whose existence is unaffected by the number of times you use them. While appropriate for manipulating pure data (numbers, strings, lists, trees, etc.), it's awkward for manipulating ephemeral computational resources, such as file descriptors or open sessions.

A language that fares better in this regard is Rust. Rust has parametric polymorphism, algebraic data types, freedom from nils (are we seeing a common theme?) and type classes, but gives up on interactive programming in exchange for precise control over data structure representation and resource management. Rust's type system design understands very well the ephemeral nature of objects: When a non-`Copy` object is passed around elsewhere, it's truly gone from the current lexical context.

---

Now, I don't want to give the impression that I think verification is the be all and end all of computer programming. There's plenty of room for languages that let you develop "fast and loose" solutions, as opposed to follow some principled philosophy. (Ruby comes to mind.) However, Go's very own marketing suggests that this isn't what they're trying to achieve. They do want a language that enforces a programming discipline. They just happen to have designed a language whose discipline doesn't provide enough benefits to justify the freedom restriction.



You are making some really good points.

One thing I would like to observe is that all the languages that you are citing that don't implement nil also implement patter-matching. All have something equivalent to the Haskell Maybe type which needs to unwrap the value upon utilization.

I would be open to admit that nil is an absolute mistake if you can demonstrate that unwrapping is as convenient as just checking for nil and then using the value (or present another mechanism that is as convenient).

In regards to possible class of errors in Haskell I can think of a few: any non-terminating program, any program that blows up the memory. It's possible to implement a Monad that doesn't obey the monadic laws. It's possible to inverse the order of arguments inadvertently if they have the same type. Any insufficiently specified type, like using Int when only a range of Int is acceptable or any String when only a URL is acceptable. I'm sure there are more ;)


> if you can demonstrate that unwrapping is as convenient as just checking for nil and then using the value.

If you want to build a largefail at any point computation that may abort at any point in the middle, you can use the MaybeT monad transformer. Or EitherT, if you want to supply an error message when you fail. Inside a `do` block, it looks just the same as imperative code. So no syntactic convenience is lost.

But the real benefit, at least in my opinion, is the ability to design APIs that allow less room for error. In the vast majority of cases, pointers and references simply aren't intended to be nullable in the first place.

> It's possible to implement a Monad that doesn't obey the monadic laws.

And it's possible to overload operators in C++ in ways that make no sense whatsoever as well. (Using the same operator for bit shifting and stream I/O, seriously?) At least Haskell has a culture of associating laws to type classes.

> It's possible to inverse[sic] the order of arguments inadvertently if they have the same type.

No worse than any other language. If anything, Haskell's type system makes more distinctions, so this specific error is less likely.

> Any insufficiently specified type, like using Int when only a range of Int is acceptable or any String when only a URL is acceptable.

Idiomatic Haskell would make custom types, with smart constructors if necessary. Using String when a URL is expected is simply bad API design in Haskell.

---

But, anyway, I'd rather not continue this subthread, since the topic is Go.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: