“What part of Milner-Hindley do you not understand?”
stackoverflow.com
stackoverflow.com
Here the first 3 rules are those of STLC. The magic happens in [Gen] and [Inst]: namely, you can generalize free type variables. If you have a function that works on lists of elements of type t when there is no constraint on t, they can work on any lists of any type. This brings quantification to the type system, ie you go from:
∀ a. reverse : α list → α list
to reverse : ∀ α. α list → α list
(in the first one, ∀ is part of the discourse, and in the second one, part of the type). [Inst] states that you can replace a quantified variable by any variable. This is the role of the ⊑ operator, which is unfortunately not explained in this notation. This is confusing for people not used to type systems, ie most people).Technically, [Let] is redundant because it is a combination of [App]+[Abs] : let x = e1 in e2 is equivalent to (λx.e2) e1. There is a difference in the ML branch of languages (as opposed to the Haskell branch) because in the presence of memory references (mutable cells) you can't generalize everywhere, so only let-bound variables are given a type scheme σ (both in the let...in construct and in toplevel phrases). That's why there is usually an explicit generalisation step at this point. Or maybe I have overlooked this particular presentation. HTH
This equivalence does not hold in the HM formalisation. It's well explained in the Wikipedia article : http://en.wikipedia.org/wiki/Hindley-Milner#Let-polymorphism
[Abs] is monomorphic while [Let] is polymorphic. Using the Wikipedia article exemple : \f.(f true, f 0) will not be typed while let f = \x.x in (f true, f 0) will be reduced to (bool, int).
(Operationally speaking, the two forms (let and abs+app) are always equivalent, though).
Would it be correct to assume, then, that a pure ML would allow HM to be trivially modified to support the equivalence?
On the contrary, the value restriction is a part of the extended rules used for the typing of ML. It was introduced because ML isn't pure, it supports references. The value restriction is there to prevent them having polymorphic type. The rule is simple : syntactic values are the only things receiving a polymorphic type. What is a syntactic value ? To quote Geoffrey Smith notes on SML97 "Basically, a syntactic value is an expression that can be evaluated without doing any computation".
All this iss done in order to ensure type safety. Actually, if references could have a polymorphic type, the type system would accept incorrect programs such as :
let r : 'a list ref = ref [] let r1 : int list ref = r let r2 : string list ref = r r2 := "a" let v : int = 1 + !r1
Which does not work because r, r1 and r2 are references to the same value.
Value restriction is a bit overkill. It rejects program which could be safely typed. Other solution to this problem exists. Actually, Standard ML used a different solution at the beginning (carrying information about the typed hold in a cell along) but it has other drawbacks. MLTon has a really good page about all that : http://mlton.org/ValueRestriction
Some research was done about relaxing value restriction. OCaml type system, for example, has less constraints than SML and MLTon (you can see this paper about that http://caml.inria.fr/pub/papers/garrigue-value_restriction-f...).
I believe that if you don't do it and generalize everywhere, unification of types is harder or even impossible because everything has a type scheme.
This would look a lot like System F, for which type inference is undecidable, for example.
If you look at the [Abs] rule, you will see in the premise "x : tau". Here, by definition, tau is non qualified. It represents a unique type. It's necessary monomorphic. Concretely, it means x will be bound to a type variable in the type environment and this type variable will uniquely be unified.
On the other hand, the premise for [Let] is e0 : sigma, x : sigma. Contrary to tau, sigma is qualified (polymorphic). So, it can be unified to a different type variable in different expressions.
I completely understand why it fails you. The way qualifier affects the unification process is quite complicated. I finally understood it only after having to write my own rules when I was implementing a type checker for my master thesis.
If you don't mind the formalism, read carefully the Wikipedia article paying particular attention to how tau and sigma are defined and to the paragraph about free and bound type variables. If you want something more practical, look at the Peyton Jones book I gave a link to in this thread. Part 9.5 is the important part but it's difficult to get without having read the whole chapter (eventually chapter 8 too). It 's still complex but far less abrupt than the Wikipedia article.
If the SPJ book is the one about implementing functional languages, then I've got a copy on my laptop I can peruse when I get home from work.
This causes difficulties because the set of free type variables needs to be calculated in order for generalisation to produce safe type schemes, and that is tough to do for an unknown set of expressions.
This is Karl Crary's explanation; here's the only online recording I've found: http://cstheory.stackexchange.com/questions/8892/type-infere...
As for this explanation, I'm not entirely convinced. In the first example, seeing x as two different cells depending on the type is interesting, but I have trouble seeing how it is possible to express its semantics without keeping runtime type information. Is System F compatible with type erasure?
So in some sense the value restriction is there to prevent us from having to make the ugly choice between unsafety on the one hand, and inefficiency and unintuitive behavior on the other.
Admittedly, it's an ASCII bastardization of the notation, but it's the same idea.
A
B
----
C
I just hate pressing everything into this style.Although I am not working in that area any more, when I used interactive theorem provers the one I used mostly would present the proofs in Sequent calculus. It showcased a nice connection between the logical framework and a small step operational semantics reading of te formulas.
In my view, it is important to understand the reasons for different fields choices of notation; it typically reveals interesting things about the fields in question. When starting out learning a new field, it often seems strange and un-intuitive, but after a while one starts to recognize that there are many valid reasons for why a certain style of notation is used. Of course, sometimes it is just an accident of history, and bad choices stick around :)
I've hopped around quite a bit in my studies (semantics, formal methods, operations research, theoretical computer science, pure algebra, logic), and I always feel overwhelmed with new notations and concepts. Nowadays I tend to expect it when trying to learn something new.
I haven't seen a notation that is easier to read than inference rules. Writing them as functional programs can be problematic, as the rules do not always fully specify an algorithm. One judgement may have different derivations. If one writes down algorithms, such as HM type inference, then one does in fact often use notation in the style of functional programming.
Incedentally, rather than abolishing the inference rule notation, there are actually tendencies to introduce them into programming: https://en.wikipedia.org/wiki/Epigram_%28programming_languag... (though it remains to be seen if this is a good idea)
In the current example, there is just no need to write Hindley-Milner in this notation. Using pattern matching and sets will do just fine.
Of course, all this is a matter of taste and for particular applications like HM it doesn't make any difference at all which notation to use. I don't want to defend the rule notation too much; it takes up a lot of space in papers and if you have something better then fine. But maybe that explains why people prefer this notation.
The horizontal bar notation is not ideal however. In my opinion Prolog notation is clearer, because it reads the right way around, and it makes it clear what relation we are defining. In Prolog notation you'd define a typeof(context, term, type) relation:
typeof(C,var(X),T) |- X:T in C
typeof(C,app(X,Y),B) |- typeof(C,X,A -> B), typeof(C,Y,A)
...
In Prolog you should read |- as "if". So the first rule says: "the type of a variable X is T if `X:T` is in the context C".(although I did once threaten my co-workers that I would tell the theoretical mathematician on the team about Unicode identifiers in Java. He used to write LaTeX in the Javadoc comments [which Doxygen handles rather nicely])
http://en.wikipedia.org/wiki/Fortress_(programming_language)
Or even mix-fix operators like Maude. Maude lets you define operators with variable arity and arbitrary stuff before, between and after, like
let _ = _ in _
_::_(_)
_,_ |- _
You _, but I _
Always considered this a cool feature....thus reducing his productivity to zero while he hunts through the Unicode maps to find the glyph he wants?
If you're using a tool which forces you to use a map to find glyphs, you're simply doing it wrong. Really, there is no excuse in this day and age not to support Unicode well!
I think this will be demise of Haskell by the way. As good as it is, the notation is still full of '||', '!!', '++', ':' ... So I think there would be another iteration in the functional world. Haskell will die, just like ML/Ocaml/etc had died, because Haskell with slightly better syntactic sugar had captured that segment of developers.
But these are not so much syntactic issues. It always seemed to me that people are actually drawn to Haskell's syntax.
Where you have a different + for doubles and ints...
It's probably difficult to strike the right balance between too little and too much overloading. Maybe OCaml does not allow enough overloading. For my taste typical Haskell code uses too much. But that's a matter of taste, I suppose.
How do type classes make that harder? You either need to understand what "foo" does, or you need to understand what "fooForThisType" does. What's hard about the former?
Are there alternative approaches I am not aware of? All I can see is encode the type in the name of the function (fmapList, fmapMaybe, fMapVector, etc), or forcing the functions to all be in separate namespaces and have to qualify them (List.fmap, Maybe.fmap, etc). Both seem much worse than typeclasses.
Totally anecdotal, but this is really not the case for me. I have only dabbled in Haskell, and never really used it for a project from end-to-end, but have used Standard ML quite a bit in the last few months to write a lexical analyzer generator [0] and just recently spent a weekend and a half using OCaml.
There are a number of Haskell features and general design that I think are better than the MLs. While modules and functors are quite cool, I think typeclasses end up being more practical. Standard ML gets a lot of little things right that I think OCaml gets flat out wrong (mutable strings, throwing exceptions instead of returning 'a option), but Haskell seems to take it further, with a standard library that I can only describe as very tasteful. This is just my impression after reading half of Real World Haskell and doing a lot of dabbling but not a lot of real work, so I'm sure it's incomplete.
Haskell's syntax IS nicer than Standard ML's and OCaml's, but that is the least important thing IMHO. If there were an ML (strict, impure) with typeclasses and with as taseful design as Haskell at the edges, I would probably use that over Haskell, even if the syntax was ugly(ier). As it stands, I'm inclined to eventually switch to using Haskell, and just figure out how to deal with the other bits of it (laziness, purity), just so I can use typeclasses and the well-designed bits.
[0] In case you doubt how much ML I've written, the repo is on GitHub
To be completely honest, I'm a little skeptical given that the first page in the "Mythryl for SML programmers" highlights adding special-cases to the front-end to support printf: http://www.mythryl.org/my-Mythryl_printf.html I don't really miss printf in SML, although if I wanted it I think I would rather reach for this kind of type hackery instead: http://mlton.org/Printf
You think haskell is going to die because it has operators? Seriously?
Wikipedia article for those who, like me, not only don't understand it but had even heard of it before.
As an aside, the other day i started flipping thru Harper's Lazy Eval writeup, and found it surprisingly readable, even if i was skipping reading the rules.
http://www.cs.cmu.edu/~rwh/plbook/book.pdf
http://www.amazon.com/Types-Programming-Languages-Benjamin-P...
Of course, I go to a university that seems very biased towards practical engineering sorts of topics over theory, so your experience may vary.
Except for basic stuff like set theory and Boolean logic, math was for the required math classes.
I didn't get the background really necessary to really understand the type-theory parts of this notation until I took a graduate course in programming language theory.
Personally, I loved studying CS (my degree was in logic so I straddled the worlds of Math and CS). It's a topic I find infinitely interesting.
Having siad that, Engineering is also very interesting. All the CS theory in the world isn't going to save you when you're trying to complete a large system full of lots of moving parts - many of whom are developers :)
At least it was so when I graduated many many moons ago.
For people interested in type checkers, there is a couple of really good chapters by written by Peter Hancock in a book available online : The Implementation of Functional Programming Languages by Simon Peyton Jones. They entirely cover the theory and there is a complete implementation in Miranda. Here is the link : http://research.microsoft.com/en-us/um/people/simonpj/papers...
In short: Often people need to try a lot of "Yeah, but what if <x>?" before they satisfy themselves that something is in fact as simple as it appears.
INIGO: You are using Bonetti's defense against me, eh?
MAN IN BLACK: I thought it fitting, considering the rocky terrain.
INIGO: Naturally, you must expect me to attack with Capo Ferro.
MAN IN BLACK: Naturally. But I find that Thibault cancels Capo Ferro, don't you?
INIGO: Unless the enemy has studied his Agrippa... which I have!
EDIT: I had not seen the movie, and I don't know how it's relevant. Obviously that means I should downvote!
Ah, discussions between set theorists and logicians, those were the days.
Benjamin Pierce's Types and Programming Languages seems to be the gold standard of intro type theory, and definitely covers this notation and the math behind it, in a much more rigorous fashion than PLAI. It is a much deeper book though, so it doesn't get to type inference and Hindley Milner until later. I've read some of it and haven't actually gotten this far yet, but can say that it is a superb textbook, it's very well written and easy for an amateur to follow, as long as you're relatively comfortable reading and writing proofs. (but you don't need more math than your standard undergrad discrete math course)
You might also want to look at Lambda the Ultimate's Getting Started page, which has a wide range of suggestions covering more than just types, but programming languages in general.
http://lambda-the-ultimate.org/node/492
http://www.cis.upenn.edu/~bcpierce/tapl/
http://cs.brown.edu/~sk/Publications/Books/ProgLangs/2007-04...
Harper hosts an online version of this book: http://www.cs.cmu.edu/~rwh/plbook/book.pdf.
http://www.amazon.com/Foundations-Programming-Languages-Comp...