Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Advanced programming languages (might.net)
131 points by Rickasaurus on May 24, 2010 | hide | past | favorite | 81 comments


Some thoughts.

The people I know who do work in machine learning (Jon Kleinberg, Thorsten Joachims, and their students) work in straight-up C, not Haskell, as the author suggests. Most of the dominant methods used today, such as the SVM, boosting, and the dynamic programming methods used in genomics are (i) numerical more than symbolic and (ii) involve moving a LOT of data around, so you want control of your data structures. (Sometimes these guys compress in-memory pointers!)

I looked long and hard at Scala and decided that I didn't like what I saw. One problem is that, based on the JVM, type erasure is a big "broken window" that seriously damages the Scala type system. You can use generics for ~years~ in Java and never notice the problems that type erasure causes, but I find that 2/3 of the designs that I specifically want an advanced type system for do not work in Scala because of type erasure.

Another issue with Scala is that the interface with Java, particularly using collections, is awkward; people who want to work in the JVM but want concision might be happier with Groovy. Scala's Lispy-Lists are convenient for the pattern matching capabilities in Scala, but are much less efficient than the vector-based Lists that come with Java when your data gets big.

As for oCaml, the real excitement is in F#. You get the wonderful oCaml language and all of its features, plus you get an interface to the C#/.NET world that's much nicer than Scala's interface to the JVM. You've got access to the whole .NET framework base class library, plus all sorts of stuff that's been written for .NET.

I'd also put C# on the border between a "mainstream" and "advanced" language. C#'s generics implementation just added support for covariant and contravariant inheritance, which is one of the features that had me interested in Scala. C# has good lambdas, really neat stuff in LINQ, and "expression trees" which offer metaprogramming capabilities above and beyond any static language I've seen.

Note that there is a good open source implementation of the .NET framework, Mono, so you can develop in C# and F# and target Linux, MacOS and other platforms.


Starting with version 2.7.2, Scala added an experimental feature (which will no longer be experimental as of 2.8.0) called Manifests that allow you to selectively reify erased types. See this blog post for more: http://www.scala-blogs.org/2008/10/manifests-reified-types.h...

This, combined with specialization (compiling primitive-specialized versions of code with generic types to improve performance of, for example, using function types on primitive collections) will make it easier to write performant, numeric code in Scala than in any other JVM language (up to the limits of the JVM, of course).

The NLP communities at Stanford and Berkeley, which do a fair amount of machine learning, have a long history of working in Java (http://nlp.stanford.edu/software/, http://nlp.cs.berkeley.edu/Main.html#Software) and have recently started using Scala as well (http://www.scalanlp.org/).

Scala's interface with Java collections IS a bit clunky. Thankfully, this is a library issue, not a language issue, and can be solved with better libraries. See http://github.com/jorgeortiz85/scala-javautils (for Scala 2.7.x) and http://github.com/scalaj/scalaj-collection (for Scala 2.8.x).


> Another issue with Scala is that the interface with Java, particularly using collections, is awkward; [...]

Really? For me one of Scala's best attributes, by far, is the ability to use its implicit conversions to make awkward Java APIs smooth like butter.

> One problem is that, based on the JVM, type erasure is a big "broken window" that seriously damages the Scala type system.

I'm not sure I understand or agree. Understand: If you don't use casting, Scala's type system seems just as strong as ML, albeit not as good at inference. (F# has the exact same inference weakness as soon as you start using OO and inheritance.) Agree: having the ability to consciously drop into a dynamically typed/runtime-casted world for small bits of code is quite nice (true for F# too). And I've yet to see a place where erasure (a) caused, even in theory, a runtime type error, or (b) lost any type information within the scala language at compile or link time (which is where the type checking lives). I think it's a red herring / mere implementation detail.

> As for oCaml, the real excitement is in F#. You get the wonderful oCaml language and all of its features, plus you get an interface to the C#/.NET world that's much nicer than Scala's interface to the JVM.

Counterpoint: I've written production code in both Scala and F#; give me Scala any day of the week. I really wish its .NET support was more mature; I'd ditch it in a heartbeat for Scala if I could.


Using implicits is really nice to clean up the interface to some Java APIs (especially for APIs making heavy usage of anonymous inner objects) but I experienced more ugly boilerplate than I would like when having to use Java collection classes from Scala, too.

To be more specific, when I want to transform a collection given by a Java library in a functional manner, I usually ended up with one or two localized import statements - to avoid e.g. scala.collection.mutable leaking into my other methods - and additional calls to convert the Java collection into a Scala specific and back again. This doesn't look like a big issue, but it increased the line count for a lot of cases from '1' to 3-5 ... which puts it uncomfortably close to 'dumb for loop territory'.

Admittedly, this is a small price to pay compared to the constant annoyance that is Java, but for my part I fear that there might be a slight tendency in Scala to fix interesting problems preferably to useful ones.

... lost any type information within the scala language at compile or link time...

How about this use case?

match { case x: List[Foo] => ... case x: List[Bar] => ... }

give me Scala any day of the week

Why do you have the preference for Scala? I'm curious because Scala is my language of choice for my personal projects, but I would ditch it the second a credible F# derivative would appear on the JVM (F#'s stronger emphasis on functional programming, cleaner syntax and abstaining from 'fixing typing' in OOP are the reasons for my preferences).


Out of curiosity, what do you mean by "fixing typing"? (I'm genuinely curious. I don't know Scala and F# well enough to really understand.)


My guess as to what the poster was about (warning: sleep deprived today, rambling ahead):

F# has basically two type systems -- the O'Caml/ML one, and C#'s. As a plus, you get full type inference so long as you stick with ML discriminated unions or records; as a minus, you lose the ability to apply concepts from OO without converting them to classes -- in which case you lose the ability to apply some handy ML-isms.

Scala tries to pull off a fairly deep unification of OO with functional types; for example making discriminated unions into case classes; or blending OO ideas of objects and classes with ML ideas about modules (including functors) into a single concept. It's far more ambitious in this regard than F#, but not necessarily an unqualified success either.


yes thats about what I meant, and more concise than my own explanation :-)


e.g. is SomeCollection<String> a subtype of SomeCollection<Object> (using Java like Syntax)? For some type of collections in some kind of circumstance this might be sensible (e.g. when your collections are immutable), sometimes having an inverse relationship might be appropriate and in some case you don't want these two types to be related at all (like Java collections - Arrays being the exceptions).

Co/Contravariance (maybe View Bounds?) in Scala enable you to encode this relationship, but from my understanding are somewhat handicapped by type erasure.

Another problem Scala tackles is that using inheritance as a 'code sharing facility' is a bit tricky (e.g. tractability and fragile base class problem) and not composable. Scala's traits and Self Types are a real improvement in this regard. That the order of mixing in traits can have an influence the objects behavior while not its type can be seen as a problem though.

F# takes the .Net object system 'as is' and introduces 'types' more aligned to functional programming (aka. discriminated unions) as separate entities. It does not try to integrate both concepts as does Scala with its case classes.


> To be more specific, when I want to transform a collection given by a Java library in a functional manner, I usually ended up with one or two localized import statements - to avoid e.g. scala.collection.mutable leaking into my other methods - and additional calls to convert the Java collection into a Scala specific and back again.

I think the other poster was right about this being more of a library issue than a language issue. In my experience it was rare to do more than wrap Java collections to or from an Iterable[]; that could be done easily without having to bring full mutable collections into the namespace.

Also, it might be part of the cost of working with "enterprise" Java, but it was actually really rare for me to work with a vanilla Java collection rather than some library's implementation of its own damn 'typesafe' iterator, like this: http://xerces.apache.org/xerces-j/apiDocs/org/w3c/dom/NodeLi.... It was super-nice to toss together my own wrappers of some library or another's semi-standard iteration API and have a ton of functionality come along via the magic of mixins; and get it all automatically applied for the cost of a single import statement via implicits.

> match { case x: List[Foo] => ... case x: List[Bar] => ... }

I suppose such a thing is made impossible via erasure, it's true. I haven't encountered a need for it -- maybe because the problem there is erasure + dynamic/runtime typing, rather than erasure on its own. Also it's something that's coming for Scala, it sounds like. (I haven't paid as much attention as I should to its ongoing development..)

> Why do you have the preference for Scala?

Part of it's just the intuitive feel: When I code in Scala, I'm writing in Scala -- it might be a fairly huge language, but it is its own thing. Coding in F# feels like bouncing back and forth between C# and O'Caml, depending on how functional you're feeling at the moment -- here's a C#-ish clause; there's an O'caml-esque one.

As an example, I don't believe you can use both inheritance and discriminated unions in F#. In Scala you can and it's actually quite useful.

Another example is that in F# you can omit type declarations on function arguments until you start using OO, then they become mandatory and infect your code. On the one hand the extra inference is nice but on the other it really drives home that you are coding in two languages, not one.

Besides that intuitive part, I think that Scala's module/class-level type system, although complicated, helps tremendously for writing big, complex, programs. Traits/mixins, flexibility in type constraints, and allowing types as members of other types add up to a really powerful ability to modularize without sacrificing. At least for me, a lot of my older O'Caml projects made fairly heavy use of functors. F# didn't bother to try to support the idea and only supports plain-vanilla C# interfaces. Scala embraced them, extended it, and made them much better.


In my experience it was rare to do more than wrap Java collections to or from an Iterable[]

Could you give me a simple example how say filtering and mapping a collection received by a java library and pushing it back to a method expecting a collection of the original type would look like? (quite likely that I overcomplicate this in my own code)


Sorry for the late reply.

Assuming you want your maps and filters to be purely functional, you need only two simple methods: One that constructs an object that implements the Iterable[X] trait from the java object, and another that constructs an instance of the java collection from an Iterable[X]. All the implementation that you'd like is already done for you in the iterable trait, but you can always selectively override them if you'd like to provide a more tailored implementation.

Then it's up to your taste whether you'd like the conversion methods to be implicit (and thus available with a single import but making your code more 'magic') or explicit (single import + adding calls to wrap/unwrap methods).

Total overhead: centralized conversion methods, assuming you write them yourself = implementation of elements(), and in the iterator, next() and hasNext(). One import per module that uses the conversions, and optionally, explicit calls to perform the conversion.


Scala 2.8 has manifests, which reify generic types. What do you wish you could do with reified types that you can't do with manifests?

Also, in Scala 2.7 I wrote my own library to wrap Java collections that didn't take much time or effort and allowed me to seamlessly pass scala collections to java code and use java collections the same as scala ones. Scala 2.8 is supposed to have such a library built in so you don't have to write it.

Lastly, your comparison between Scala's functional lists and Java's ArrayList is pointless. Scala has an ArrayBuffer class too, so you can choose between either as appropriate. You can also pattern match against ArrayBuffer as well.


Note that there is a good open source implementation of the .NET framework, Mono, so you can develop in C# and F# and target Linux, MacOS and other platforms.

Is Mono actually usable with F#? The last time I checked (2 months ago) the F# plugin for MonoDevelop didn't work and the questions on how to getting it to work (by various people) went unanswered by the developer.

The runtime performance of F# code was shaky at best, the REPL had (on OS X at least) startup times to make it practically useless and without TCO the usefulness of F# really took a hit.

Not wanting to bash on Mono or F# (which is my favorite language), but writing production software with this combination is currently not something I would recommend.


[Disclaimer: I'm a compiler dev working on F# at MSFT.]

Performance and user experience on Mono are something we're definitely trying to improve - we spent a fair amount of time trying to make our Mono story better for our 2.0 release, and plan on investing more in this area as we move forward.

We can't be everywhere at once, so your feedback is really important here - If you're having problems with our tools on Mono, please let us know via Microsoft Connect or fsbugs@microsoft.com. Also, don't be bashful about filing Mono bugs. So far, they've been really great about responding to any issues that have cropped up.

Thanks!


Thats awesome! Thanks for sharing. I'll give Mono/F# another spin in the next week and will report problems as suggested by you. Again thanks.


On http://github.com/vasili/FSharpBinding is an FSharpBinding that works with MonoDevelop. Please read the README to under- stand current functionality. I have tested with new F# projects plus existing F# projects like Mandelbrot sets, Stephen Wolfram's Rule 30, etc. There is still a fair amount of polishing that needs to be done. That is a work-in-progress.

Kind regards,

Vasili I. Galchin


Hello,

     On http://github.com/vasili/FSharpBinding is an FSharpBinding that works with MonoDevelop. Please read the README on this web site. I have tested new F# projects and several existing F# projects like Mandelbrot set, Stephen Wolfram's Rule 30, etc. This binding still needs polishing .... working on that with other colleagues.
Kind regards,

Vasili I. Galchin


I was curious if F# is really that close to ocaml, and it seems they are related: http://stackoverflow.com/questions/179492/f-and-ocaml with some differences (eg. F# doesn't seem to have those crazy +. *. etc float operators for type inference).

Here's a ridicuously detailed table comparing F#, ocaml, haskell, scala, ML (ie. everything in the article but scheme), at the aptly named http://hyperpolyglot.wikidot.com/ml


> The people I know who do work in machine learning work in straight-up C, not Haskell, as the author suggests.

Is their work in applying ML theory, or creating more of it? ML almost always involves large datasets in practical use, but when you're trying to come up with a new method for ensuring correctness in some edge-case, you would be testing it with hypothetical extreme inputs, not feeding it mounds of data that won't trigger the problem. Haskell works well to model computer science research problems—that is, research into CS, not using CS to research other domains. Once your model has "solidified", you should by all means optimize it in C before running it on real-world data.


Yeah, it's clear that more than a bit of PL research has fed back into C# as a language and .NET as a whole, in stark contrast to Java/JVM.


  It has been my experience that it is difficult to get a bug passed the
  Hindley-Milner type system.
Amusingly, this typo (passed instead of past) is an example of the challenge facing language and tool developers. We can make a checking system isomorphic to a spell-checking system, but then we need a grammar-checking system to ensure that even if a word is correctly spelled, it forms a legal sentence. Once we have that, we need some sort of logic-checking system to make sure that a word with the correct spelling and the correct grammar is meaningful in the program. And so forth.

It's type-checking all the way up.


Even with perfect grammar, spelling, and logic checks, you're still going to lose sometimes:

I mistyped a single letter in an email and offended a coworker for life. He was on the road; I was in the office. I argued persuasively about a difference in design, ending with "When you get it, we can talk some more". I had intended to say "When you get in, we can talk some more".

http://news.ycombinator.com/item?id=1375231


I once commented on something, intending to say "Uh, have you considered..." But I wrote:

Ugh. Have you considered...

I had some 'splainin to do!


But that's the thing about Hindley-Milner — most typos and logic errors will result in a different type than the one you intended. You'll get an infinite type or a number where you meant to get a list, etc. There are some bugs that can make it through, but the number of errors the type system can catch is amazing — it's actually close to being a logic checker.


And a strong sell for Scala and no mention of Clojure. It confuses the hell out of me; clojure is fast, mature, and has a OO syntax and model to rival CLOS. It's got built in laziness and te best concurrency primitives I've seen to date (and I was a professional Erlang programmer).

What does it take to break into the general public's consciousness? Clearly it's not the parens.


This article is from December 2008, when both languages were fairly young but Scala had been around significantly longer than Clojure.


Good point. I hadn't noticed that.


<contributor-bias>

I seriously think Clojure is too much to take in at the first introduction. It's really hard to get your brain around everything done right, when compared to traditional programming. You need to use it for a while to really get why it's approach to state (not concurrency) is awesome.

</contributor-bias>


I think that this may be a side-effect of the literature, which is very lispy and computer sciency. For someone like me, that's exactly the right tack. For someone who may not take for granted all the claims Rich and his book-writing compatriots are making (let alone take for granted the lisp code-as-data is an foundational revelation of computer science), it may be a problem.

Because really, Clojure is in many ways simpler than both Scala and Java. You can ignore the hard parts (agents, deftype, macros, de-chunking seqs, etc) and just cruise through the easy parts with a fair degree of ease. Everyone understands functions, after all. And Javascript has really pushed the "use functions for everything" approach into the public eye quite nicely.


That, and we're still getting our act together in the Clojure world. Stay tuned :)


I keep asking for, and I will do so again here, a non-toy web application written in clojure as a study object for newbies to dissect. That would help a lot.


What would qualify as non-toy? With Ring and a routing library like Moustache what more do you really need for building a non-trivial web application in Clojure?


Well, you outline the problem really nicely there. Never heard of Moustache, and the problem is in figuring out how all the bits go together and what is the 'best' way of doing this in clojure. The learning curve for the language alone is steep enough that having a non-trivial example would do wonders in terms of understanding how a clojure based website works under the hood.


If you've ever worked with light weight http framework in Python or Ruby, it works exactly the same way.

http://mmcgrana.github.com/2010/03/clojure-web-development-r... http://github.com/cgrand/moustache

There isn't a mature web framework for Clojure yet (for obvious reasons), so there isn't a popular "way". At the moment you have a lot of good libraries and you can put your app together in whatever way seems most sound to you.


I agree. I have been working on some statistical NLP code in Clojure today for a client and I continue to be amazed at how concise code can be. One of the reasons I starting using Ruby about 5 years ago was that it is such a concise language, but Clojure seems even better. Smaller code is easier to read and maintain.


Scala has Twitter to champion it in business and tech circles as well as creator Odersky's technical university affiliations to appeal to the academic crowd.

Clojure has a lot going for it in terms of theory but I don't know that it can claim any of the social proof of Scala.

The closest thing I can think of for Clojure is all of Siebel's Lispy interview questions from Coders at Work that lead an underinformed coder to think highly of STM and of Lisps.

Edit: Scala apparently predates Clojure by a good four years. The larger mindshare is no surprise in that light. Also it appears to have simple pattern matching and several other things that Alex Payne liked: http://www.slideshare.net/al3x/why-scala-presentation


AFAIK the "theory" in clojure also stems from the scala world in that clojure data structures are based on work done for scala or by scala affiliates.


No. All that work predates both, in some cases by several years.

In fact, a good friend of mine is working to put scala bindings over all the clojure data structures so they can be used transparently in scala. They're that good.


Your friend might be wasting a lot of effort (apart from his personal learning experience), as Phil Bagwell is working on Scala's collection library.


For context to "outsiders": Bagwell wrote the papers that the Clojure datastructures are based on.


“A lot of effort?”

Wrapping the PersistentMaps took like less than 200 lines of code: http://github.com/codahale/yoink


Yes, 'a lot' is quite relative. :-)

From my point of view 200 LoC giving me something I already have is a lot. From the point of view of your friend 200 LoC might be a very small price to pay for a good learning experience.


I invite you to benchmark the two.

I'm also slightly confused as to why you think reusing stable, proven, and reviewed data structure implementations is a "good learning experience" as opposed to being SOP.


scala.collection.immutable is/will not be stable, proven and reviewed?


The hash array mapped trie was committed to trunk 3 months ago and was first released in 2.8.0.RC1 (which no one uses due to various bugs) and is now available in 2.8.0.RC2 (which very few people are using due to binary incompatibility).

In contrast, Clojure's PersistentHashMap dates back to 2007 and was in both the 1.0 and 1.1.0 releases.


Is it unreasonable to expect that Scala's persistent collections will work comparably to Closure's when 2.8 final is released?


That doesn't sound unreasonable, no -- at some point in the future, Scala's hash array mapped trie will be robust and highly optimized. If your plan is to wait until then, you might be able to speed things up by helping them out yourself.

In the meantime, I benchmarked Yoink's PeristentHashMap against 2.8.0.RC2's immutable.HashMap by a) creating a map of 100,000 sequential keys and then removing them all, and b) creating a map of 100,000 random keys and then removing 100,000 random keys. In the first case, Yoink was 20.7041% (±1.83693%) faster. In the second case, Yoink was 24.7834% (±4.17055%) faster. (Student's t distribution, 95% confidence interval, source is in src/test on the GitHub project and I used ministat for the statistical analysis [a cross-platform port of which is also available on my GitHub page].)

So to summarize, I have a provably faster hash array mapped trie at almost zero cost right now because I spent a few hours with a "good learning experience."


I'm not disputing that Clojure's data structures right now are more performant than Scala's or that your project was worthless. I even apologized in case my original post was badly worded.

What I wanted to confer was that, you _might_ have been wasting a lot of effort because I assumed that you might not have been informed about work of the Scala team that has a high likelihood of obsoleting your project in the near future. Obviously you were informed and my notice was inappropriate. Be assured it won't happen again.

If increasing the performance by about 1/4 for your use cases - compared to the current beta - is valuable for you, great! For me it wouldn't have been worth a few hours. 'A good learning experience' would have. But obviously your values are different.


The back of your hand is going to get sore with all those compliments.


I did not have the intention of backhanding you or your friend. If my post had the appearance of doing so, I apologize.

I just wanted to point out the (for me at least) non obvious fact that the mind behind the theory of Clojure's persistent datastructures is working on Scala's collection library and that Scala's hash maps are already backed by hash tries - http://www.scala-lang.org/archives/downloads/distrib/files/n...


Good for Scala. When it's ready, people can use that. Until then, there are interim solutions.

And it's pretty neat to see a scala proponent call a scala project a "waste of time." Way to represent for your community, bro.


I liked the look of Clojure the language, but I got put off a bit by all the Java infrastructure that gets pulled in. For example, not knowing much about Java build tools, I have very little idea what leinigen is actually doing behind the scenes. There's not much documentation, and I didn't have much luck googling for error messages. I'm playing around a bit with Google App Engine, and things got a lot easier when I just gave up and switched over to Python. Having to use any kind of build system feels like a huge waste of time if execution speed is not a top priority. Python might not be quite as nice a language as Clojure, but I just write the code and it works -- no extra fiddling about required.


I think that you are correct about two things: Clojure stack traces and error messages are pretty bad. And, Python is probably the best language for AppEngine because of the small loading request times; with Java I struggle to minimize loading request times (I use objectify-appengine instead of JDO, etc.)


I think haskell is worth watching. If projects like repa and dph are able to deliver parallel performance speedups, then haskell could become a top choice for machine learning and related algorithms.

Personally, I have found haskell to be hard to program in initially, but once you get started, refactoring and feature changes feel "easy".


>[Scheme is] untyped, which makes it ideal for web-based programming and rapid prototyping.

Erm... While Scheme can be useful for web and prototyping, it is typed. That's why R5RS requires functions like number->string and so on.


Some people use "type" exclusively to refer to compile-time entities. By this definition, languages that are commonly referred to as "dynamically-typed" are actually "untyped", because they have only one compile-time type, which is essentially the same as having no types.


That's confusing weak/strong with static/dynamic typing, though. (A common error.)

"What To Know Before Debating Type Systems" (http://www.pphsg.org/cdsmith/types.html) is a pretty good introduction to types.

Also, while I'm only partway through Benjamin Pierce's _Types and Programming Languages_, it's been quite good so far.


Actually, your link agrees with my explanation:

It points out in the "Static and Dynamic Types" section that what is meant by "type" in static type-systems and what is meant by "type" in dynamic type-systems are two very different concepts. From the perspective of someone interested in static "types", dynamically-typed languages don't have types at all.


Specifically, this is the definition used in Computer Science research. Unfortunately, the communication between that community and the larger community of programmers is so poor that it was several years after I finished my CS bachelor's degree before I figured out why some people insisted that languages like Python were "untyped".


It's rather semantic, but I would differentiate typed from untyped by type checking vs. type being an inherent property of a variable.

So you have something in Scheme like:

     (define foo 1)
     (define bar "1")
And that is typed, the former being a number, the latter a string. If you try to subsequently do:

     (+ foo bar)
the program will error out.

Contrast this with what I would consider an untyped language like Python or Perl, where the translation from string to number is done without any warnings showing up.


Python is as strongly typed as Scheme; 1+"1" will produce an exception in Python.


Thanks. Point stands on Perl. Also... BASIC, among others.


What exactly does "typed" mean? Clearly there's a difference (in every language I know of) between True and 3.5 (boolean vs. float). Unless the language treats all memory locations equally and doesn't know anything about a memory location other than its pattern of 0's and 1's, I would consider the language "typed."


What "typed" means depends on who you're talking to. There's the definition used in CS research papers, and the definition that programmers actually use.

Very few languages lack a strong notion of run-time type. C and friends are the only ones I can think of where you can treat a block of memory as more than one type without the compiler or runtime throwing a fit.


I'm curious where all the data is about "expressiveness" being so important. I'm yet to meet the person who can churn out functional solutions quicker than imperative... but I hope this is my naivete and not just the CS bandwagon effect.


Some data about Lisp vs. Java/C/C++:

http://www.norvig.com/java-lisp.html

Not clear how much is due to the functional nature of Common Lisp, but does show a clear difference between programming languages in expressiveness and productivity.


I smiled when I read this:

Haskell's type system literally allows the compiler to infer the correct code to run based on its type context

It literally has overloading! Imagine that!


How many other programming languages have a type system that supports overloading on both parameter types and the desired return type?


Languages with operator overloads, including typecast ops.


Most languages with operator overloads don't have inferred static type-checking woven through them, though.


On the contrary, almost all statically typed languages with overloaded operators use type inference to type subexpressions; it's how they do type checking and produce type errors. Type inference for named values, or locations, or functions is one thing; but type inference of expressions is everywhere. If you consider '+' as overloaded on ints, floats etc., even C uses type inference to infer a type for '4 + 0.5'.


I don't know if I'd call that "inference" so much as coercion, precedence and propagation. The exciting thing about Haskell type classes is precisely the integration with the type system and its modified HM type inference -- if you don't think the Haskell type system is cool, you probably won't be impressed much by its fairly elegant support for an abstraction that lets you overload operators, perform multidispatch, enforce contracts, and define the very laws of mathematics.

Perhaps if you don't know Haskell, you should learn Haskell to understand what the article is saying.

Edit: This might help: http://www.haskell.org/tutorial/classes.html


I already know Hindley-Milner type inference, and I do know Haskell well enough to understand typeclasses, monads, etc.

I also believe that type inference is of limited practical application at scale. On a local level, internal to a module or function, it can make a lot of sense, but it's at risk of underspecification at the module interface level. For example, when you modify function bodies, you may inadvertently add more constraints to an inferred type (e.g. use an operator or function defined on a typeclass not previously brought in), and consequently break clients of the module.


This is definitely something that any interface author must deal with and is very closely related to the more OO-specific skill of "good interface design". It is important to include in an interface those types and type constraints that effectively and efficiently describe your problem domain.

OCaml (and potentially Haskell, though I haven't used this feature in Haskell) ameliorates this problem by giving the programmer the facility to specify (or generate and edit based on inference) module signatures that are then checked against. With this approach, I would argue that type inference actually makes your large-scale code more robust and easier to modify than without it due to the automatic verification of module signature. If you inadvertently introduce extra type constraints to your module's interface, the type-checker will tell you and save you from releasing a module that is incompatible with previous versions of the same module.

Without type inference, you're either annotating types everywhere (looking at you Java...) or waiting until runtime (or production!) to find out you've made an error (e.g. perl, python, ruby and on and on).

I really don't see how type inference and its associated program analysis and verification is anything but good.


To give a concrete example: The printf function in Haskell takes on the responsibilities of both the traditional printf (printing to the screen) and sprintf (generating a formatted string). Haskell knows which it's supposed to do based on whether the return value is used in an IO context or as a string.


Which is, when you think about it, not a good idea. If 'sprintf' is defined, then 'printf' ought to be simply 'putStr . sprintf'.


But sprintf is not defined in Haskell, nor is it really needed. printf returns a typeclass that includes both 'String' and 'IO a'.


My point is that there should be no typeclass; there's no reason to combine IO and formatting in the same function, and doing so makes it more difficult to read and modify code.


Agreed. This is something I dislike about Haskell. After seeing the regex class and it tons of contexts I was just thinking "didn't they learn anything from perl?".


Amusingly enough, I tried to sell two of the books he pictures there at my garage sale last weekend...


Considering the context, it is amusing to see no mention of arc.




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

Search: