<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
		>
<channel>
	<title>Comments on: Universal Type Inference is a Bad Thing</title>
	<atom:link href="http://www.codecommit.com/blog/scala/universal-type-inference-is-a-bad-thing/feed" rel="self" type="application/rss+xml" />
	<link>http://www.codecommit.com/blog/scala/universal-type-inference-is-a-bad-thing</link>
	<description>(permanently in beta)</description>
	<lastBuildDate>Sun, 29 Aug 2010 20:01:44 -0700</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
		<item>
		<title>By: obiwankenobi</title>
		<link>http://www.codecommit.com/blog/scala/universal-type-inference-is-a-bad-thing/comment-page-1#comment-4914</link>
		<dc:creator>obiwankenobi</dc:creator>
		<pubDate>Sat, 12 Dec 2009 21:01:16 +0000</pubDate>
		<guid isPermaLink="false">http://www.codecommit.com/blog/scala/universal-type-inference-is-a-bad-thing#comment-4914</guid>
		<description>OCaml has a much better type inference than ML, because, among others, it uses the operator +. -. *. and /. for floats as opposed to + - * and / for integers.

And of cource, you can add the types explicitly anywhere to make finding errors easier.</description>
		<content:encoded><![CDATA[<p>OCaml has a much better type inference than ML, because, among others, it uses the operator +. -. *. and /. for floats as opposed to + &#8211; * and / for integers.</p>
<p>And of cource, you can add the types explicitly anywhere to make finding errors easier.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Daniel Spiewak</title>
		<link>http://www.codecommit.com/blog/scala/universal-type-inference-is-a-bad-thing/comment-page-1#comment-4856</link>
		<dc:creator>Daniel Spiewak</dc:creator>
		<pubDate>Thu, 16 Jul 2009 00:57:20 +0000</pubDate>
		<guid isPermaLink="false">http://www.codecommit.com/blog/scala/universal-type-inference-is-a-bad-thing#comment-4856</guid>
		<description>@Zifre

The real problem, which I didn&#039;t directly address in the article, is the fact that Hindley-Milner-style type inference removes the type error from its origin, sometimes quite far from the origin.  As you say, we would immediately see the problem with my contrived example the moment we tried to use the function.  However, who&#039;s to say that we&#039;ll be able to easily track down what&#039;s going on?  For example, suppose that we used the function in the following way:

&lt;pre&gt;fun contrived nil n = n + 42
  &#124; contrived (_::_) n = sum [1, 2, 3] n&lt;/pre&gt;

This produces the following error:

&lt;pre&gt;stdIn:1.5-5.41 Error: types of rules don&#039;t agree [literal]
  earlier rule(s): &#039;Z list * int -&gt; int
  this rule: &#039;Z list * int list -&gt; int list
  in rule:
    (_ :: _,n) =&gt; (sum (1 :: &lt;exp&gt; :: &lt;exp&gt;)) n&lt;/pre&gt;

Sure, we&#039;re finding the problem eventually, but I dare you to track that one down without squinting hard, particularly if the definition of `sum` is in a different module.

This example *is* contrived.  However, I have run into very similar issues when trying to write real code, and that&#039;s what bothers me.  If people aren&#039;t religious about annotating their types -- and, unlike Haskell, ML doesn&#039;t make a point of encouraging this -- it is very easy to dig yourself into a very confusing and hard-to-trace error just by virtue of repeated false inferences.</description>
		<content:encoded><![CDATA[<p>@Zifre</p>
<p>The real problem, which I didn&#8217;t directly address in the article, is the fact that Hindley-Milner-style type inference removes the type error from its origin, sometimes quite far from the origin.  As you say, we would immediately see the problem with my contrived example the moment we tried to use the function.  However, who&#8217;s to say that we&#8217;ll be able to easily track down what&#8217;s going on?  For example, suppose that we used the function in the following way:</p>
<pre>fun contrived nil n = n + 42
  | contrived (_::_) n = sum [1, 2, 3] n</pre>
<p>This produces the following error:</p>
<pre>stdIn:1.5-5.41 Error: types of rules don't agree [literal]
  earlier rule(s): 'Z list * int -> int
  this rule: 'Z list * int list -> int list
  in rule:
    (_ :: _,n) => (sum (1 :: &lt;exp&gt; :: &lt;exp&gt;)) n</pre>
<p>Sure, we&#8217;re finding the problem eventually, but I dare you to track that one down without squinting hard, particularly if the definition of `sum` is in a different module.</p>
<p>This example *is* contrived.  However, I have run into very similar issues when trying to write real code, and that&#8217;s what bothers me.  If people aren&#8217;t religious about annotating their types &#8212; and, unlike Haskell, ML doesn&#8217;t make a point of encouraging this &#8212; it is very easy to dig yourself into a very confusing and hard-to-trace error just by virtue of repeated false inferences.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Zifre</title>
		<link>http://www.codecommit.com/blog/scala/universal-type-inference-is-a-bad-thing/comment-page-1#comment-4855</link>
		<dc:creator>Zifre</dc:creator>
		<pubDate>Wed, 15 Jul 2009 23:28:19 +0000</pubDate>
		<guid isPermaLink="false">http://www.codecommit.com/blog/scala/universal-type-inference-is-a-bad-thing#comment-4855</guid>
		<description>I also disagree. This is contrived because you never use the function (which would catch the error). However, I agree that you should normally put on type annotations. The nice thing about making them optional is that it allows you to annotate application/library code for documentation and error checking, while still allowing you to quickly write small scripts without annotations (giving it a dynamic language like feel).</description>
		<content:encoded><![CDATA[<p>I also disagree. This is contrived because you never use the function (which would catch the error). However, I agree that you should normally put on type annotations. The nice thing about making them optional is that it allows you to annotate application/library code for documentation and error checking, while still allowing you to quickly write small scripts without annotations (giving it a dynamic language like feel).</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Jonathan Shapiro</title>
		<link>http://www.codecommit.com/blog/scala/universal-type-inference-is-a-bad-thing/comment-page-1#comment-4278</link>
		<dc:creator>Jonathan Shapiro</dc:creator>
		<pubDate>Thu, 06 Nov 2008 06:56:00 +0000</pubDate>
		<guid isPermaLink="false">http://www.codecommit.com/blog/scala/universal-type-inference-is-a-bad-thing#comment-4278</guid>
		<description>I also think that the example is contrived, but mainly because you presuppose that the programmer never looks at the inferred type to see if it makes any sense.

My larger objection is to your assertion that partial annotation somehow isn&#039;t &quot;real&quot; type inference. This seems silly. The purpose of type inference isn&#039;t to prevent you from writing types. The purpose is to help get the types cross-checked and confirmed. How much of the automation you choose to use is completely up to you. None of this absolves you from looking at the answes.

Annotations are not a panacea either -- and particularly not in a language having implicit coercions. I cannot tell you how many bugs in the SVR4 kernel implementation were due to disagreements between two structures concerning which integer size to use. All the annotations were there, but the errors were not caught automatically. Needless to say these were not easy to track down.

I suppose the thing to say here is that all languages have shortcomings. The test of ML, in part, is that the types of errors you decry are rarely observed in the wild.</description>
		<content:encoded><![CDATA[<p>I also think that the example is contrived, but mainly because you presuppose that the programmer never looks at the inferred type to see if it makes any sense.</p>
<p>My larger objection is to your assertion that partial annotation somehow isn&#8217;t &#8220;real&#8221; type inference. This seems silly. The purpose of type inference isn&#8217;t to prevent you from writing types. The purpose is to help get the types cross-checked and confirmed. How much of the automation you choose to use is completely up to you. None of this absolves you from looking at the answes.</p>
<p>Annotations are not a panacea either &#8212; and particularly not in a language having implicit coercions. I cannot tell you how many bugs in the SVR4 kernel implementation were due to disagreements between two structures concerning which integer size to use. All the annotations were there, but the errors were not caught automatically. Needless to say these were not easy to track down.</p>
<p>I suppose the thing to say here is that all languages have shortcomings. The test of ML, in part, is that the types of errors you decry are rarely observed in the wild.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Greg M</title>
		<link>http://www.codecommit.com/blog/scala/universal-type-inference-is-a-bad-thing/comment-page-1#comment-3454</link>
		<dc:creator>Greg M</dc:creator>
		<pubDate>Thu, 10 Apr 2008 03:47:45 +0000</pubDate>
		<guid isPermaLink="false">http://www.codecommit.com/blog/scala/universal-type-inference-is-a-bad-thing#comment-3454</guid>
		<description>I can&#039;t agree with this at all. As soon as you try to _call_ your ML function, you&#039;ll get an error message telling you that it doesn&#039;t have the type you think it does. If you&#039;ve messed up the calling function too, maybe your problem will be a bit harder, but it&#039;s still just a matter of learning when to query the type of something while debugging. And learning where explicit type declarations make sense - at fixed interface boundaries. It&#039;s more than worth it to avoid the duplication and change-friction of defining your internal types too early.</description>
		<content:encoded><![CDATA[<p>I can&#8217;t agree with this at all. As soon as you try to _call_ your ML function, you&#8217;ll get an error message telling you that it doesn&#8217;t have the type you think it does. If you&#8217;ve messed up the calling function too, maybe your problem will be a bit harder, but it&#8217;s still just a matter of learning when to query the type of something while debugging. And learning where explicit type declarations make sense &#8211; at fixed interface boundaries. It&#8217;s more than worth it to avoid the duplication and change-friction of defining your internal types too early.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Daniel Spiewak</title>
		<link>http://www.codecommit.com/blog/scala/universal-type-inference-is-a-bad-thing/comment-page-1#comment-3453</link>
		<dc:creator>Daniel Spiewak</dc:creator>
		<pubDate>Wed, 09 Apr 2008 19:28:10 +0000</pubDate>
		<guid isPermaLink="false">http://www.codecommit.com/blog/scala/universal-type-inference-is-a-bad-thing#comment-3453</guid>
		<description>The example is pretty contrived, but I&#039;ve written ML code in the past which has had similar (but far more complex) issues caused by over-aggressive type inference.  The problem is that such a universal type inference system often separates the error from the root cause, leading to confusion down the line.

The point has been raised that ML (and Haskell) allows you to explicitly annotate types in the function declaration.  So my example could be redeclared like this:

fun sum nil init:int = init 
 &#124; sum (hd::tail) init = hd + (sum tail init)

This simple change is sufficient to avoid all of the issues with cons vs plus in the typo.  However, this is not really type inference anymore.  Certainly the compiler is inferring a number of types in this expression, but it is no longer inferring *everything*.  In both my ML and my Scala examples, I used the *least* type annotations possible to still allow things to compile.  Thus, in both examples I relied as heavily as possible on type inference in the respective languages.  By annotating a type, you&#039;re effectively overriding the type inference and giving it explicit information to draw on.

I suppose type annotations in ML really are a matter of policy enforcement as much as any other convention is.  Hindley/Milner is a very powerful form of type inference which allows a lot of bad code (like my contrived example).  It is a bit of a subjective thing, but I tend to believe that a language should not be *too* enabling.  It&#039;s like static vs dynamic typing.  Dynamic type systems are inarguably more flexible due to the removal of static constraints, but are they really better in a real-world application?  You can certainly make policies that stipulate documentation and &quot;good design&quot; as it relates to potential abuse of the dynamic type system (e.g. runaway duck typing), but the potential still remains for something very bad to happen.  ML allows many dubious practices (such as not annotating function params etc), and when the compiler doesn&#039;t enforce something, lazy programmers tend to take advantage of the loophole.

As a matter of record, ML doesn&#039;t require you to type everything into the interpreter.  There are tools such as Compilation Manager which allow the compilation of such ML functions without actually spitting out the type signature at compile-time.  It is quite conceivable that a developer could write a function like my example, compile it using a tool such as CM, and never realize the issue until some point later in the code when he attempted to use the function.  Once again, the error is separated from the cause.

@Martin

I&#039;m personally quite pleased with Scala&#039;s type inference and really it&#039;s type system in general.  There are certainly times when I *wish* it had more universal type inferencing, but in general I prefer it just the way it is.  The notable exception of course being recursive methods.  :-)

As to MLF, I was actually pointed that direction shortly after I wrote this article by a professor in type theory with whom I correspond.  Remy&#039;s premise seems to be very similar to the point I was trying to make in this article, except he did more than just point out the problem and actually developed a complete language extension to fix the &quot;problem&quot;.</description>
		<content:encoded><![CDATA[<p>The example is pretty contrived, but I&#8217;ve written ML code in the past which has had similar (but far more complex) issues caused by over-aggressive type inference.  The problem is that such a universal type inference system often separates the error from the root cause, leading to confusion down the line.</p>
<p>The point has been raised that ML (and Haskell) allows you to explicitly annotate types in the function declaration.  So my example could be redeclared like this:</p>
<p>fun sum nil init:int = init<br />
 | sum (hd::tail) init = hd + (sum tail init)</p>
<p>This simple change is sufficient to avoid all of the issues with cons vs plus in the typo.  However, this is not really type inference anymore.  Certainly the compiler is inferring a number of types in this expression, but it is no longer inferring *everything*.  In both my ML and my Scala examples, I used the *least* type annotations possible to still allow things to compile.  Thus, in both examples I relied as heavily as possible on type inference in the respective languages.  By annotating a type, you&#8217;re effectively overriding the type inference and giving it explicit information to draw on.</p>
<p>I suppose type annotations in ML really are a matter of policy enforcement as much as any other convention is.  Hindley/Milner is a very powerful form of type inference which allows a lot of bad code (like my contrived example).  It is a bit of a subjective thing, but I tend to believe that a language should not be *too* enabling.  It&#8217;s like static vs dynamic typing.  Dynamic type systems are inarguably more flexible due to the removal of static constraints, but are they really better in a real-world application?  You can certainly make policies that stipulate documentation and &#8220;good design&#8221; as it relates to potential abuse of the dynamic type system (e.g. runaway duck typing), but the potential still remains for something very bad to happen.  ML allows many dubious practices (such as not annotating function params etc), and when the compiler doesn&#8217;t enforce something, lazy programmers tend to take advantage of the loophole.</p>
<p>As a matter of record, ML doesn&#8217;t require you to type everything into the interpreter.  There are tools such as Compilation Manager which allow the compilation of such ML functions without actually spitting out the type signature at compile-time.  It is quite conceivable that a developer could write a function like my example, compile it using a tool such as CM, and never realize the issue until some point later in the code when he attempted to use the function.  Once again, the error is separated from the cause.</p>
<p>@Martin</p>
<p>I&#8217;m personally quite pleased with Scala&#8217;s type inference and really it&#8217;s type system in general.  There are certainly times when I *wish* it had more universal type inferencing, but in general I prefer it just the way it is.  The notable exception of course being recursive methods.  <img src='http://www.codecommit.com/blog/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p>As to MLF, I was actually pointed that direction shortly after I wrote this article by a professor in type theory with whom I correspond.  Remy&#8217;s premise seems to be very similar to the point I was trying to make in this article, except he did more than just point out the problem and actually developed a complete language extension to fix the &#8220;problem&#8221;.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: matthiasB</title>
		<link>http://www.codecommit.com/blog/scala/universal-type-inference-is-a-bad-thing/comment-page-1#comment-3445</link>
		<dc:creator>matthiasB</dc:creator>
		<pubDate>Wed, 09 Apr 2008 15:57:35 +0000</pubDate>
		<guid isPermaLink="false">http://www.codecommit.com/blog/scala/universal-type-inference-is-a-bad-thing#comment-3445</guid>
		<description>Stupid HTML that messed up all the &lt;s</description>
		<content:encoded><![CDATA[<p>Stupid HTML that messed up all the &lt;s</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Runar</title>
		<link>http://www.codecommit.com/blog/scala/universal-type-inference-is-a-bad-thing/comment-page-1#comment-3442</link>
		<dc:creator>Runar</dc:creator>
		<pubDate>Wed, 09 Apr 2008 13:39:45 +0000</pubDate>
		<guid isPermaLink="false">http://www.codecommit.com/blog/scala/universal-type-inference-is-a-bad-thing#comment-3442</guid>
		<description>Your example is terribly contrived. The job of the type system is to check that types are consistent. It&#039;s exceedingly unlikely that your function would exist in a vacuum where its type would never be composed with some expression that uses it. A simple property like (sum [0] 0 == 0) would suffice. And besides, nothing stops you from putting a type declaration for the ML function.

Careful about throwing out terms like &quot;Bad Thing&quot;. Bad for whom, and to what end? A perfectly valid reply to your subject line would be: &quot;No it isn&#039;t.&quot; It&#039;s actually quite useful.</description>
		<content:encoded><![CDATA[<p>Your example is terribly contrived. The job of the type system is to check that types are consistent. It&#8217;s exceedingly unlikely that your function would exist in a vacuum where its type would never be composed with some expression that uses it. A simple property like (sum [0] 0 == 0) would suffice. And besides, nothing stops you from putting a type declaration for the ML function.</p>
<p>Careful about throwing out terms like &#8220;Bad Thing&#8221;. Bad for whom, and to what end? A perfectly valid reply to your subject line would be: &#8220;No it isn&#8217;t.&#8221; It&#8217;s actually quite useful.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: matthiasB</title>
		<link>http://www.codecommit.com/blog/scala/universal-type-inference-is-a-bad-thing/comment-page-1#comment-3440</link>
		<dc:creator>matthiasB</dc:creator>
		<pubDate>Wed, 09 Apr 2008 11:04:15 +0000</pubDate>
		<guid isPermaLink="false">http://www.codecommit.com/blog/scala/universal-type-inference-is-a-bad-thing#comment-3440</guid>
		<description>I think a big problem in Scala would be subtyping. 

Imagine you&#039;d write a (stupid) function to concatenate two lists:

def concat(xs, ys) = xs match {
   case nil =&gt; ys
   case hd::tl =&gt; hd::concat(tl, ys)
}


What&#039;s the type of the function? Is it

def concat[T](xs:List[T], ys:List[T]):List[T] = xs match {
   case nil =&gt; ys
   case hd::tl =&gt; hd::concat(tl, ys)
}

Without subtyping this might be the right type, but with subtyping you might prefere something like this:

def concat[T, U  ys
   case hd::tl =&gt; hd::concat(tl, ys)
}

This might be the right type, but the type inferencer could as well infere this:

def concat[T, U  ys
   case hd::tl =&gt; hd::concat(tl, ys)
}

This is a valid type for the function, but IMO has no advantage over the previous one. But perhaps this is the type thet will be infered. And now imagine a more complicated function than this very simple one. Especialy in combination with higher order functions you might get uncomprehensably complex types.</description>
		<content:encoded><![CDATA[<p>I think a big problem in Scala would be subtyping. </p>
<p>Imagine you&#8217;d write a (stupid) function to concatenate two lists:</p>
<p>def concat(xs, ys) = xs match {<br />
   case nil =&gt; ys<br />
   case hd::tl =&gt; hd::concat(tl, ys)<br />
}</p>
<p>What&#8217;s the type of the function? Is it</p>
<p>def concat[T](xs:List[T], ys:List[T]):List[T] = xs match {<br />
   case nil =&gt; ys<br />
   case hd::tl =&gt; hd::concat(tl, ys)<br />
}</p>
<p>Without subtyping this might be the right type, but with subtyping you might prefere something like this:</p>
<p>def concat[T, U  ys<br />
   case hd::tl =&gt; hd::concat(tl, ys)<br />
}</p>
<p>This might be the right type, but the type inferencer could as well infere this:</p>
<p>def concat[T, U  ys<br />
   case hd::tl =&gt; hd::concat(tl, ys)<br />
}</p>
<p>This is a valid type for the function, but IMO has no advantage over the previous one. But perhaps this is the type thet will be infered. And now imagine a more complicated function than this very simple one. Especialy in combination with higher order functions you might get uncomprehensably complex types.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Christophe Poucet (v</title>
		<link>http://www.codecommit.com/blog/scala/universal-type-inference-is-a-bad-thing/comment-page-1#comment-3439</link>
		<dc:creator>Christophe Poucet (v</dc:creator>
		<pubDate>Wed, 09 Apr 2008 10:09:27 +0000</pubDate>
		<guid isPermaLink="false">http://www.codecommit.com/blog/scala/universal-type-inference-is-a-bad-thing#comment-3439</guid>
		<description>This is an unfair comparison.  In Haskell or ML you can annotate your top level functions. And indeed, often this is done, mostly for documentation purposes as well as localizing type-errors more quickly.

You annotated the top-level of Scala but did not do so with the ML version.  You concretely gave more information to the Scala compiler than the ML compiler.

In Haskell, since this is the language I&#039;m currently most familiar with, I would annotate it as follows.  Note that typically accumulator-based recursion is not used in Haskell, but I will do so now to keep the changes minimal.  Note also that this function works for any number type, not just ints, thereby making it more flexible than the Scala version.  I renamed the function as the function &#039;sum&#039; already exists.

mysum :: (Num a) =&gt; [a] -&gt; a -&gt; a
mysum [] init = init
mysum (x:xs) init = x + (sum xs init)

If we now introduce the error you introduced:
mysum :: (Num a) =&gt; [a] -&gt; a -&gt; a
mysum [] init = init
mysum (x:xs) init = x:(sum xs init)

You will now get a compiler error on the third line.</description>
		<content:encoded><![CDATA[<p>This is an unfair comparison.  In Haskell or ML you can annotate your top level functions. And indeed, often this is done, mostly for documentation purposes as well as localizing type-errors more quickly.</p>
<p>You annotated the top-level of Scala but did not do so with the ML version.  You concretely gave more information to the Scala compiler than the ML compiler.</p>
<p>In Haskell, since this is the language I&#8217;m currently most familiar with, I would annotate it as follows.  Note that typically accumulator-based recursion is not used in Haskell, but I will do so now to keep the changes minimal.  Note also that this function works for any number type, not just ints, thereby making it more flexible than the Scala version.  I renamed the function as the function &#8217;sum&#8217; already exists.</p>
<p>mysum :: (Num a) =&gt; [a] -&gt; a -&gt; a<br />
mysum [] init = init<br />
mysum (x:xs) init = x + (sum xs init)</p>
<p>If we now introduce the error you introduced:<br />
mysum :: (Num a) =&gt; [a] -&gt; a -&gt; a<br />
mysum [] init = init<br />
mysum (x:xs) init = x:(sum xs init)</p>
<p>You will now get a compiler error on the third line.</p>
]]></content:encoded>
	</item>
</channel>
</rss>
