
Hindley-Milner in Clojure - ericn
http://www.lispcast.com/Hindley-Milner-in-Clojure
======
exDM69
Here's the Hindley-Milner implementation (in Haskell) from a toy compiler
project of mine. It was really enlightening to write it and surprisingly
simple.

[https://github.com/rikusalminen/funfun/blob/master/FunFun/Ty...](https://github.com/rikusalminen/funfun/blob/master/FunFun/TypeChecker.hs)

This was also the first time I used monad transformers and almost the first
non-IO monad application (I've used ST and Parsec before) I have dealt with.
If you compare my code with the book source (Peter Hancock's type checker in
Peyton-Jones' "Implementation of Functional Programming Languages", link in
source code comment), my version using monads is a lot simpler to follow than
the original, written in a pre-Haskell functional programming language called
Miranda with no monads.

The type checker is a "pure function", it has inputs and outputs but no side-
effects but in the code you need to 1) generate unique "names" 2) bail out
early on type errors. I solved this problem using Error and State monads. The
Miranda code used an infinite list of numbers for unique names and cumbersome
tricks to handle type errors.

~~~
sfvisser
Nice job! Seems you could have put the TypeEnv in a ReaderT as well, might
make things slightly easier.

------
willismichael
I find it curious that the venn diagram seems to indicate that a sizable
subset of people who are familiar with type theory don't advocate either
static typing or dynamic typing.

~~~
octo_t
I love type theory, but I think a language like Sage[1] is probably the most
interesting way forward.

[1] - [http://sage.soe.ucsc.edu/](http://sage.soe.ucsc.edu/)

~~~
tel
How do you feel about dependently typed systems?

~~~
ufo
Sage has dependent types so he probably likes them.

~~~
tel
Ah, I misread the intro blurb.

------
hardboiled
Disagree about the idea that those who are unfamiliar with type theory prefer
dynamic typing.

Typing preferences are usually due to trends in language usage having little
to do with knowledge.

Plenty of java programmers use static typing without ever having to understand
type theory.

But looking to history of language designers/implementers

Dan Friedman

Gilad Bracha [http://www.infoq.com/presentations/functional-pros-
cons](http://www.infoq.com/presentations/functional-pros-cons)

Guy Steele

Rich Hickey

All of these guys have worked on static languages, have a keener understanding
of type theory than most, and yet they seem to promote dynamic languages at
least when it comes to their pet languages.

~~~
the_af
I'm not disagreeing with you (that a lot of people go with what's trendy or
what they already know), but I wouldn't put Gilad Bracha in the list of
knowledgeable people. I've seen the talk you are linking to and it's not very
impressive... he sounds mostly whiny to me. In his own blog, when he writes
about about functional programming or type theory, he gets called out by the
people who really know about it.

~~~
codygman
I would agree, I don't see why Gilad Bracha is on that list.

------
nabla9
Why not both?

ghc compiler in Haskell has -fdefer-type-errors flag. SBCL Common Lisp
implementation has option to turn type warnings into errors. Extending
-fdefer-type-errors function and creating better type checker for dynamic
languages could achieve best of both worlds.

~~~
ufo
The actual reason for why not both is that its actually very hard to do and is
still kind of an open problem. Some things are easy to do statically but hard
to check dynamically (for example, parametric polymorphism or type-based
overloading and name resolution) and some things are easy to check dynamically
but hard to specify using static types (for example, array bounds checking).

~~~
nabla9
You can do those things that are easy to do statically at development/testing
time and those thing that are easy to do dynamically in runtime.

Some things like overloading based on return values don't work dynamically,
but you could either choose to not have them or resolve them in development
time. I would like to see languages where dynamic vs. static is continuum and
programmers can determine how much they need want case by case basis.

~~~
ufo
> You can do those things that are easy to do statically at
> development/testing time and those thing that are easy to do dynamically in
> runtime.

But then you lose the ability to freely mix and match the static and dynamic
code. For example, if you want to write a dynamic implementation for some
parametrically polymorphic code there must be a way to check at runtime that
the dynamic implementation is respecting the type that you assigned to it
(there are ways to do that but they are all very tricky).

The list of tricky things also grows very quickly once you put some though
into it. You mentioned return type polymorphism but there are also many other
things that are hard to check dynamically (record field names) and lots of
things that dynamic codes likes to do (like mutability and subtyping) that
force extra complexity into the static type system or that can be used to
subvert the static types if you are not careful.

------
chongli
Dynamic typing is just a special case of static typing where there is only one
type!

~~~
sfvisser
Even when this is true, this special case of static typing gives you exactly
none of the advantages of general static typing.

~~~
chongli
Yes, of course. I'm a big proponent of static typing, or (as Bob Harper likes
to call it) typing! This is in contrast with unityped languages.

------
__--__
All this talk about formal type theory, but where are the references to the
relevant studies? Where's the data? The few studies[1][2][3][4] I've found are
inconclusive one way or the other and none of them focus on error rates. I
found another conversation about how to go about studying error rate in
dynamically vs statically typed languages, but all I really found was this
article studying the affect of hair style on language design[5].

[1] [http://pleiad.dcc.uchile.cl/papers/2012/kleinschmagerAl-
icpc...](http://pleiad.dcc.uchile.cl/papers/2012/kleinschmagerAl-icpc2012.pdf)
\- maintainability

[2]
[http://dl.acm.org/citation.cfm?id=2047861&CFID=399382397&CFT...](http://dl.acm.org/citation.cfm?id=2047861&CFID=399382397&CFTOKEN=13654132)
\- development time

[3]
[https://courses.cs.washington.edu/courses/cse590n/10au/hanen...](https://courses.cs.washington.edu/courses/cse590n/10au/hanenberg-
oopsla2010.pdf) \- development time, take 2

[4] [http://pleiad.dcc.uchile.cl/papers/2012/mayerAl-
oopsla2012.p...](http://pleiad.dcc.uchile.cl/papers/2012/mayerAl-
oopsla2012.pdf) \- usability

[5] [http://z.caudate.me/language-hair-and-
popularity/](http://z.caudate.me/language-hair-and-popularity/)

~~~
exDM69
I glanced over the studies you linked to and in all of them, the languages
used as examples of static typing are Java, C and C++. There's no mention of
type inference or any languages that have a more advanced static typing scheme
like ML or Haskell. A lot of it seemed to be a "Java vs. Ruby fight" with a
slight bias towards the latter in the authors.

To joke and exaggerate a little, those studies seem to be done by people who
belong to the "proponents of dynamic typing" and not "familiar with type
theory" bin of people in the Venn diagram in the OP.

~~~
__--__
I agree completely, that's why I used the word "inconclusive." Still, it's the
only real data we have on static vs dynamic typing. Until we get better data,
everything is just opinion and preference. Well informed opinions, in the case
of those familiar with type theory (which I am not), but opinion nonetheless.

~~~
exDM69
I'm afraid that when it comes to hard science, the static vs. dynamic typing
discussion will remain as "inconclusive" for a long time.

The testing methodology in (some of) the studies above involved test subjects
to be working in toy prototype programs during a short period of time. In my
opinion, this will favor dynamic typing. If there's a simple problem with a
quick'n'dirty solution even I do often prefer Python to Haskell.

The real advantages of static typing become apparent only when a project
matures as time and effort are spent on maintenance and refactoring. In
dynamic languages it is rather easy to break "old" code by making changes to
"new" code and subtly changing some types and you have to rely on unit testing
to catch this at run time. This class of errors is caught by a sane type
checker before you even start running your tests.

So my somewhat informed opinion is that it is very hard to get "conclusive"
evidence of the superiority of static typing because it's a very difficult
thing to objectively measure in a short period of time. I say "superiority"
because that's my opinion that static typing is a little better for most but
not all applications.

------
kd0amg
_I think you should implement Hindley-Milner in the language of your choice
for a small toy λ-calculus._

Did this a little while ago (as a stepping stone to building an inference
system for a more complicated calculus).

[https://gist.github.com/jrslepak/6158954](https://gist.github.com/jrslepak/6158954)

------
moomin
As an aside, if you just want curried functions in Clojure, try poppea.

[https://github.com/JulianBirch/poppea](https://github.com/JulianBirch/poppea)

~~~
derengel
Noob question, doesn't partial provides curried functions?

~~~
lmkg
A 'true' curried function in clojure doesn't need _partial_ because it
overloads on the arity of the function. It automatically partializes itself if
it's called with too few arguments.

E.g. you have a function _f_ that takes two arguments, _x_ and _y_. If the
function is truly curried, then (f x y) is a full function call of f that
returns a value, while (f x) returns a function of one argument, as if you had
used _partial_. You could create a (two-argument) currying helper-function
with the following:

    
    
      (defn curry-2 [f]
            (fn ([x] (partial f x))
                ([x y] (f x y))))
    

Basically with a currying function, (f x y) and ((f x) y) are equivalent
calls, without the need for _partial_.

The reason this isn't more pervasive is because it doesn't play well with
optional parameters, &rest parameters, arity overloading, or other ways in
which the number of arguments to a function might vary (Haskell permits
currying by not allowing variable param lists). Clojure has a couple of
library functions that use this pattern (notably, the _reducer_ library), but
it's not ubiquitous.

~~~
moomin
Indeed, that's exactly what poppea does. The code is a generalisation of the
code in the reducers library.

------
michaelochurch
I'm familiar with type theory and (often) a proponent of dynamic typing.

It depends on what you're doing. If you're building cathedrals-- high-quality,
performance-critical software that can never fail-- then static typing is a
great tool, because it can do things that are very hard to do with unit
testing, and you only pay the costs once in compilation. There are plenty of
use cases in which I'd want to be using a statically typed language like OCaml
(or, possibly, Rust).

If you're out in the bazaar-- say, building a web app that will have to
contend with constant API changes and shifting needs, or building distributed
systems designed to last decades without total failure (that may, like the
Ship of Theseus, have all parts replaced) despite constant environmental
change-- then dynamic typing often wins.

What I like about Clojure is that, being such a powerful language, you can get
contracts and types and schemas but aren't bound to them. I like static typing
in many ways, but Scala left me asking the question, any time someone insists
that static typing is necessary: _which_ static type system?

~~~
hesselink
I've heard this argument a lot, and I disagree. If your software is changing a
lot, that is where types really shine. Refactoring is a breeze when you have
types: you just change the code you want to improve, and all use sites are
pointed to by the compiler. This is taken from daily experience: I work on a
code base that is about 25K lines of Haskell and 35K lines of Javascript.
Refactoring the Haskell is a pleasure. Refactoring the Javascript is something
we dread, and always introduces bugs, some of which might linger for up to a
year.

~~~
tel
I think on top of this the self-documenting properties of a nice statically
typed system push back the onset of code ossification quite a lot. I _always_
trust my type documentation while I tend to be a skeptic of documentation
that's more than a few months old unless it's being actively refactored
regularly.

~~~
Periodic
A strong type system can give you a lot of "free" documentation and I love it!
I long for a search engine like Hoogle in other languages
([http://www.haskell.org/hoogle/](http://www.haskell.org/hoogle/)). Most
functions are very obvious from their signature and name.

My favorite example are two functions with the signatures:

    
    
        fun1: a -> a
        fun2: Int -> Int
    

You might think the first is more powerful because it takes any type and
returns that type, but ultimately, that function can only be the identify
function (barring unusual things like exceptions, undefined values and runtime
introspection). With the second function it could do all sorts of things. It
might increment, it might decrement, it might divide by two and round down if
the argument is a multiple of three.

I'll go further to say that it's very frustrating in languages like C++ and
Java to have to deal with statics and objects. They have a lot of hidden state
that isn't obvious from the type signature of their methods. Particularly the
fact that some methods must be called before others—e.g. initializers—can make
the behavior and side-effects non-obvious.

------
elwell
Douglas Crockford is a proponent of dynamic typing. (At least from what I read
in the beginning of "JavaScript: The Good Parts)

~~~
Semiapies
What's the relevance of that point? He doesn't appear to be mentioned in the
post.

