6 ms·
OCaml has a similar type system to Haskell's. It has algebraic data types. It also has all the normal higher order functions and combinators "done right". It al
by tel 13y ago
OCaml has a similar type system to Haskell's. It has algebraic data types. It also has all the normal higher order functions and combinators "done right". It also commits strongly to using first class modules as an organizational component atop the functional core.
I don't think "functional" means a whole lot. Many languages with lightweight, first-class lambdas like to jump on that bandwagon by implementing map/fold/scan/take combinators on lists, sometimes even lazy ones, but that whole game is just a sideshow to the real meat of what makes Hindley-Milner typed languages with ADTs quite fun to work in.
- andreasvc 13y agoHM type inference is one possible aspect of functional programming, but there's also the Lisp family of languages which are characterized by macros and homoiconicity, as opposed to purity and static typing. Functional programming doesn't seem to have a single definition, but the languages do share a family resemblance: instead of one or more properties shared by all members, each pair of members shares some properties.
- tel 13y agoI'm more in the camp that "functional" is just too much an umbrella term to mean much. I think it's completely fair to say that anything with light syntax for lambdas and the regular "list combinator zoo" of (map/fold/zip/unfold/scan/take) is "functional" enough for me. All the other features are orthogonal and specialized.
- seanmcdirmid 13y agoHindley-Milner is fun until you need nominal subtyping or mutable assignment, then the whole "semi-unification is undecidable in general" comes up to bite you.
- tel 13y agoHow does HM get in the way of mutable assignment?
- seanmcdirmid 13y agoAssignment is essentially similar to subtyping, if one says: A := B Then all values that could be bound to B could be bound to A also (so B <= A), but if the other way was also taken (B = A), then A := C Would imply that B = C, which leads to all sorts of safety problems...so you don't do that. At the end of the day, to make HM work with assignment, you need something like ML's "value restriction" that prevents full unification between A and B.
- gsg 13y agoNitpick: it's generalisation that is prevented by the value restriction. Unification works just fine: let test r = r := `A; r := `B; r := `C r gets unified with all of `A, `B and `C: no problem.
- emillon 13y agoPolymorphism (more precisely, generalization of unknown types) is incompatible with references. For example: let r = ref [] Here r has type 'a list ref (reference to a list of 'a where 'a is unknown). r := ["hello"] r has type 'a list ref, := has type 'a ref -> 'a -> unit, and ["hello"] has type string list, so it works (if we instantiate 'a with string). let n = 1 + (List.head !r) 1 has type int, + has type int -> int -> int, and we can type (List.head !r) as int since we can type !r as int list since we can type r as int ref list (this time instantiating 'a by int). So, we just added an int and a string and the program will probably crash. What's wrong is that every line is correct but because of the mutable store, the program as a whole is incorrectly typed. The solution is to generalize only a subset of all syntactic constructs. The historic solution in Ocaml is to never generalize expressions that may allocate memory (that's the "value restriction") . For example, a function application (like f x, or ref []) may allocate, but a variable or a constant (like x or 1 or []) can not. That's why [] has type 'a list but ref [] has type '_a list ref ('_a can be unified only once, so in the above example an error would occur at the "let n").
- tel 13y agoAhh, that's sort of obvious in retrospect, but I was blinded thinking in terms of Haskell where IO protects against such generalization.
- detrino 13y agoYep, and also overloading, overall HM just isn't a good choice for a programming language.
- codygman 13y agoCare to list the reasons why you believe HM just isn't a good choice for a programming language?
- detrino 13y agoIt limits the type system in order to achieve global type inference. HM is often touted as a "sweet spot" in static type systems because you don't have to specify types anywhere, but in reality it tends to be more verbose and less powerful than local type inference.
- codygman 13y agoThat hasn't been my experience, could you show me a small example?
- deleted 13y ago[deleted]
- detrino 13y agoIn an HM system there is no overloading so you are essentially naming type constraints on every function call instead of once in the function definition. C++ might look like this: void do_something(bar_t & bar) { ... bar.foo(...); bar.baz(...); auto something = bar.get_something(); ... } The equivalent Ocaml might look like this: let do_something bar = ... Bar.foo bar ...; Bar.baz bar ...; let something = Bar.get_something bar in ... The situation is similar when dealing with structs and tends to be ever worse when dealing with generic code (although C++ only has duck typed generics until concepts comes along).
- coolsunglasses 13y agoWeird points to make. Subtyping isn't a good organizing principle for code reuse from a type-safety or human-sanity perspective. Typeclasses and composition are better. You should give Haskell a whirl.
- detrino 13y agoHaskell doesn't use Hindley-Milner. Edit: Also, sub-typing tends to make a lot more sense in a language with value semantics, such as C++, than it does in a language like Haskell and isn't necessarily about organizing code.
- tel 13y agoWhat do you mean by value semantics here?
- deleted 13y ago[deleted]
- detrino 13y agoC++ gives you the freedom to choose between composition and subtyping to suit your problem without having to worry about an extra indirection if you choose composition. For example, the base member of Composed is not a pointer as it might be in a language with reference semantics: struct Base { int i; }; struct Derived : Base { bool b; }; struct Composed { Base base; bool b; };
- fzltrp 13y agoYes, I think the main selling point of ML languages is the type inference mechanism. Haskell doesn't support exactly the same mechanism, but it has typeclasses, which is very cool too.
- nousernamesleft 13y agoTypeclasses don't interfere with type inference. > :t (+ 5) (+ 5) :: Num a => a -> a
- tel 13y agoWell, they do: show . read
- nousernamesleft 13y agoNo they don't. > :t read . show read . show :: (Read c, Show a) => a -> c
- tel 13y agoThat's not what I typed: I did `show . read` which is type `String -> String` and has an internal, ambiguous type.
- nousernamesleft 13y agoOops. So, I'm even more confused now. You know show. read is inferred just fine, and that means typeclasses break inference?
- kd0amg 13y agoYou know show. read is inferred just fine The "internal, ambiguous type" matters when you try to actually invoke (show . read) on some String. If you want to actually use that function, you're stuck inserting a type annotation, which would lead many people to say that (show . read) is not "inferred just fine."