Hacker News new | past | comments | ask | show | jobs | submit login

Semi nit (but semantically quite important): not `Optional<A> implements Monad<A>`, but `Optional implements Monad`. The reason many languages can't encode the `Monad` type class correctly is due to this fact: type constructors like `Optional` (higher-kinded types, in the lingo) are second-class in a lot of languages, so you can't refer to them directly in your type class definitions/implementations. The practical upshot of this is that (similar to what's touched upon in the article) if you have a true `Monad` constraint you can use the `Monad` operations for any choice of `A` without requiring a different constraint for each possible `A` (which you would otherwise need, because there's no guarantee that `Optional<A> implements Monad<A>` implies that also `Optional<B> implements Monad<B>)`. If you have sufficient machinery available (universal constraints, basically) you can hack around the lack of HKTs by using a constraint like `forall A. Optional<A> implements Monad<A>` and having some kind of type-level map function that gets you from `Optional<A>` to `Optional<B>` for any `A` and `B`. This is the trick people use in Rust, for example.



> there's no guarantee that `Optional<A> implements Monad<A>` implies that also `Optional<B> implements Monad<B>

In a language like Java (and presumably many others) a generic type argument is implicitly universally quantified. Optional<A> implementing Monad<A> would therefore imply that Optional<B> implements Monad<B>.

The problem for why you can't define "monad" as an interface in Java is because you can't refer to the type constructor (due to the lack of HKT, as you yourself explained). That means that you can't even define "map" generically. You can define it for Optional, or List:

  class Optional<A> {
    ...
    Optional<B> map(Function<A, B> f) { ... }
    ...
  }

  class List<A> {
    ...
    List<B> map(Function<A, B> f) { ... }
  }
but you can't extract map into a Monad interface because you'd have to write the return type as "T<B>" or something and that's not possible (for a non-concrete T).


The universal quantification works for defining the _implementation_, but (by itself) not for defining the constraints. Yes, if you write an implementation for `Optional<A>` then it _is_ implemented for any `A` — but it's impossible for an implementation to _require_ it to be implemented for every `A` (and thereby be able to take advantage of that fact, e.g. to be able to know that `Optional<A> → Optional<B>` is well-defined for any `A` and `B`). :)




Join us for AI Startup School this June 16-17 in San Francisco!

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

Search: