3 ms·
As far as I can tell, Standard ML doesn't have much of a feature overlap problem. Off the top of my head, the only redundant feature is `abstype`, which I think
by catnaroek 11y ago
As far as I can tell, Standard ML doesn't have much of a feature overlap problem. Off the top of my head, the only redundant feature is `abstype`, which I think is a leftover from the 70's, when ML didn't have its module system. If Standard ML had first-class modules, then modules and records could also be considered redundant.
What Standard ML suffers from is lack of expressivity. The lack of (pattern) guards sometimes forces pattern matching blocks to (nest more than necessary and) contain redundant clauses. The lack of polymophic recursion forces non-uniformly recursive data types (as well as the functions that operate on them) to be rewritten in a way that makes the type checker a less effective bug-finding tool. Without higher-rank types, you can't express the Day convolution, free Applicatives, or Traversals (from the lens library). On the other hand, I don't find myself missing higher-kinded types much, since they are mostly useful in combination with type classes, which modules can adequately replace.
What 1ML does is unify features: (0) records and let expressions are now particular uses of modules, (1) core language functions and type constructors are now particular uses of functors.
- Ericson2314 11y ago> On the other hand, I don't find myself missing higher-kinded types much... I don't find myself missing modules that much for precisely the inverse reason. > If Standard ML had first-class modules, then modules and records could also be considered redundant. If Haskell+Backpack had first class packages/modules, then packages/modules and type classes could also be considered redundant. This is the duality I am trying to get it :). Looking each language alone, the features are "somewhat orthogonal". But in the context of the entire design space, the lack of unification comes off as redundancy.
- catnaroek 11y ago> I don't find myself missing modules that much for precisely the inverse reason. The “tiny” detail is that ML modules do much more than what type classes do: they allow you to define abstract types whose representation is invisible to the rest of the program, without ugly hacks like `newtype`-wrapping the internal representation. > If Haskell+Backpack had first class packages/modules Packages and modules are completely different things. A package is a unit of distribution, whereas a module is a unit of encapsulation. Even within a single program that's only distributed as a whole, you may want different components to hide their internals from each other. > This is the duality I am trying to get it There is no duality between modules and type classes, because, while modules can do everything type classes do (and more), the converse does not hold.
- Ericson2314 11y agoRemember, it's the combination of type classes and higher kinded types that the MLs are up against. [Not because there is an inherent trade-off (because there isn't one between type classes vs modules either as the Dryer paper shows), but beause the MLs have that weakness.]
- catnaroek 11y agoNon-recursive higher-kinded types can be implemented in terms of functors. For example, the free monad type constructor is very easy to implement in Standard ML (OCaml is similar): signature FUNCTOR = sig type 'a t val map : ('a -> 'b) -> 'a t -> 'b t end functor FreeMonad (F : FUNCTOR) = struct datatype 'a t = Pure of 'a | Free of 'a t F.t (* operations go here *) end Modules can do everything type classes can do, and more. There is no weakness. The real reason why you need to define this as a higher-kinded type constructor in Haskell (`data Free f a = ...`) is to overcome the limitation of not having parameterized modules. The way it's done in Haskell also has the defect that the type system will happily allow you to construct a value of type `Free Foo a`, where `Foo` is not a functor, whereas the ML version prohibits this by construction.
- Ericson2314 11y agoThat's not the same however, because the modules language is a lot more constrained than the term language. See the first paragraph the second page of https://www.mpi-sws.org/~rossberg/1ml/1ml-extended.pdf https://www.mpi-sws.org/~rossberg/1ml/1ml-extended.pdf.
- catnaroek 11y ago> That's not the same however, because the modules language is a lot more constrained than the term language. You don't need to tell me. That's obvious to anyone who has programmed in ML. But Haskell's type class language is similarly a lot more constrained than its term language. It's basically a pure (in particular, cut-free) compile-time Prolog, with some restrictions to guarantee that no query will return more than one result. > See the first paragraph the second page of https://www.mpi-sws.org/~rossberg/1ml/1ml-extended.pdf https://www.mpi-sws.org/~rossberg/1ml/1ml-extended.pdf. Rest assured, I've read the paper - not today, but several months ago. I'm quite familiar with the ML module literature. Check my post history: https://news.ycombinator.com/item?id=10856201 https://news.ycombinator.com/item?id=10856201 https://news.ycombinator.com/item?id=11131265 https://news.ycombinator.com/item?id=11131265 But, since you're arguing that the combination of type classes and higher-kinded types can do something that ML modules can't, I'm providing evidence for the opposite: everything that can be done with type classes and higher-kinded types can be done with modules. (0) You can't compute new modules at runtime, but you also can't define new type class instances at runtime. (1) An ML module can have both transparent and abstract type components, controlled by the programmer. A type class always has abstract type components at definition time, and transparent type components at instantiation time. Modules are more expressive than type classes. (2) The signature of an ML module is structural, and you can, after the fact, selectively forget part of a module's contents. Type classes don't let you do this. Try making `Monoid` a subclass of `Semigroup` without recompiling any existing code. Again, modules are more expressive than type classes. As for higher kinds, at least with OCaml's higher-order applicative functors, you can encode arbitrary higher-kinded type constructors. And most of the higher-kinded constructors that actually arise in practice can be encoded even with Standard ML's first-order functors.