<div>I sent this message yesterday to Bulat but it was intended for the haskel cafe, so I'm resending it here today.</div>
<div> </div>
<div>Thank to everyone who answered me privately. Today I'll keep on experimenting and read the reference you gave me.</div>
<div> </div>
<div>Cristiano</div>
<div><br>---------- Forwarded message ----------<br><span class="gmail_quote">From: <b class="gmail_sendername">Cristiano Paris</b> <<a href="mailto:cristiano.paris.ml@gmail.com">cristiano.paris.ml@gmail.com</a>><br>
Date: Jun 21, 2007 6:20 PM<br>Subject: Re: Type classes vs C++ overloading Re: [Haskell-cafe] Messing around with types [newbie]<br>To: Bulat Ziganshin <<a href="mailto:Bulat.Ziganshin@gmail.com">Bulat.Ziganshin@gmail.com
</a>><br><br> </span></div>
<div><span class="q"><span class="gmail_quote">On 6/21/07, <b class="gmail_sendername">Bulat Ziganshin</b> <<a onclick="return top.js.OpenExtLink(window,event,this)" href="mailto:bulat.ziganshin@gmail.com" target="_blank">
bulat.ziganshin@gmail.com</a>> wrote:</span>
<blockquote class="gmail_quote" style="PADDING-LEFT: 1ex; MARGIN: 0px 0px 0px 0.8ex; BORDER-LEFT: #ccc 1px solid">Hello Cristiano,<br><br>Thursday, June 21, 2007, 4:46:27 PM, you wrote:<br><br>> class FooOp a b where<br>
> foo :: a -> b -> IO ()<br>><br>> instance FooOp Int Double where<br>> foo x y = putStrLn $ (show x) ++ " Double " ++ (show y)<br><br>this is rather typical question :) </blockquote>
<div> </div></span>
<div>I knew it was... :D</div>
<div><span class="e" id="q_1134f1583a801b44_3"><br>
<blockquote class="gmail_quote" style="PADDING-LEFT: 1ex; MARGIN: 0px 0px 0px 0.8ex; BORDER-LEFT: #ccc 1px solid"> unlike C++ which resolves any<br>overloading at COMPILE TIME, selecting among CURRENTLY available<br>overloaded definitions and complaining only when when this overloading
<br>is ambiguous, type classes are the RUN-TIME overloading mechanism<br><br>your definition of partialFoo compiled into code which may be used<br>with any instance of foo, not only defined in this module. so, it<br>cannot rely on that first argument of foo is always Int because you may
<br>define other instance of FooOp in other module. "10" is really<br>constant function of type:<br><br>10 :: (Num t) => t<br><br>i.e. this function should receive dictionary of class Num in order to<br>return value of type t (this dictionary contains fromInteger::Integer->t
<br>method which used to convert Integer representation of 10 into type<br>actually required at this place)<br><br>this means that partialFoo should have a method to deduce type of 10<br>in order to pass it into foo call. Let's consider its type:
<br><br>partialFoo :: (FooOp t y) => y -> IO ()<br><br>when partialFoo is called with *any* argument, there is no way to<br>deduce type of t from type of y which means that GHC has no way to<br>determine which type 10 in your example should have. for example, if
<br>you will define<br><br>instance FooOp Int32 Double where<br><br>anywhere, then call partialFoo (5.0::Double) will become ambiguous<br><br>shortly speaking, overloading resolved based on global class<br>properties, not on the few instances present in current module. OTOH,
<br>you build POLYMORPHIC functions this way while C++ just selects<br>best-suited variant of overloaded function and hard-code its call<br><br>further reading:<br><a onclick="return top.js.OpenExtLink(window,event,this)" href="http://homepages.inf.ed.ac.uk/wadler/papers/class/class.ps.gz" target="_blank">
http://homepages.inf.ed.ac.uk/wadler/papers/class/class.ps.gz</a><br><a onclick="return top.js.OpenExtLink(window,event,this)" href="http://haskell.org/haskellwiki/OOP_vs_type_classes" target="_blank">http://haskell.org/haskellwiki/OOP_vs_type_classes
</a><br>chapter 7 of GHC user's guide, "functional dependencies" </blockquote>
<div> </div></span></div></div>
<div>Mmmmmhhhh... your point is hard to understand for me.<br> </div>
<div>In his message, I can understand Bryan Burgers' point better (thanks Bryan) and I think it's somewhat right even if I don't fully understand the type machinery occuring during ghc compilation (yet).</div>
<div> </div>
<div>Quoting Bryan:</div>
<div> </div>
<div>"<em>From this you can see that 10 is not necessarily an Int, and 5.0 is<br></em>not necessarily a Double. So the typechecker does not know, given just<br>10 and 5.0, which instance of 'foo' to use. But when you explicitly
<br>told the typechecker that 10 is an Int and 5.0 is a Double, then the<br>type checker was able to choose which instance of 'foo' it should use."</div>
<div> </div>
<div>So, let's see if I've understood how ghc works:</div>
<div> </div>
<div>1 - It sees 5.0, which belongs to the Fractional class, and so for 10 belonging to the Num class.</div>
<div>2 - It only does have a (FooOp x y) instance of foo where x = Int and y = Double but it can't tell whether 5.0 and 10.0 would fit in the Int and Double types (there's some some of uncertainty here).</div>
<div>3 - Thus, ghci complains.</div>
<div> </div>
<div>So far so good. Now consider the following snippet:</div>
<div> </div>
<div>module Main where</div>
<div>
<p>foo :: Double -> Double<br>foo = (+2.0)</p>
<p>bar = foo 5.0<br><br>I specified intentionally the type signature of foo. Using the same argument as above, ghci should get stuck in evaluating foo 5.0 as it may not be a Double, but only a Fractional. Surprisingly (at least to me) it works!
</p>
<p>So, it seems as if the type of 5.0 was induced by the type system to be Double as foo accepts only Double's.</p>
<p>If I understand well, there's some sort of asymmetry when typechecking a function application (the case of foo 5.0), where the type signature of a function is dominant, and where typechecking an overloaded function application (the original case) since there type inference can't take place as someone could add a new overloading later as Bulat says.
</p>
<p>So, I tried to fix my code and I came up with this (partial) solution:</p>
<p>module Main where</p><span class="q">
<p>class FooOp a b where<br> foo :: a -> b -> IO ()</p></span>
<p>instance (Num t) => FooOp t Double where<span class="q"><br> foo x y = putStrLn $ (show x) ++ " Double " ++ (show y)</span></p>
<p>partialFoo :: Double -> IO ()<br>partialFoo = foo 10</p>
<p>bar = partialFoo 5.0</p>
<p>As you can see, I specified that partialFoo does accept Double so the type of 5.0 if induced to be Double by that type signature and the ambiguity disappear (along with relaxing the type of a to be simply a member of the Num class so 10 can fit in anyway).
</p>
<p>Problems arise if I add another instance of FooOp where b is Int (i.e. FooOp Int Int):</p>
<p>module Main where</p><span class="q">
<p>class FooOp a b where<br> foo :: a -> b -> IO ()</p></span>
<p>instance (Num t) => FooOp t Double where<span class="q"><br> foo x y = putStrLn $ (show x) ++ " Double " ++ (show y)</span></p>
<p>instance (Num t) => FooOp t Int where<br> foo x y = putStrLn $ (show x) ++ " Int " ++ (show y)</p>
<p>partialFoo = foo 10</p>
<p>bar = partialFoo (5.0::Double)<br><br>while I thought could be solved likewise:</p>
<p>module Main where</p><span class="q">
<p>class FooOp a b where<br> foo :: a -> b -> IO ()</p></span>
<p>instance (Num t1, Fractional t2) => FooOp t1 t2 where<span class="q"><br> foo x y = putStrLn $ (show x) ++ " Double " ++ (show y)</span></p>
<p>instance (Num t1, Num t2) => FooOp t1 t2 where<br> foo x y = putStrLn $ (show x) ++ " Int " ++ (show y)</p>
<p>partialFoo = foo 10</p>
<p>bar = partialFoo 5.0</p>
<p>but it didn't work. Here's ghci's complaint:</p>
<p>example.hs:7:0:<br> Duplicate instance declarations:<br> instance (Num t1, Fractional t2) => FooOp t1 t2<br> -- Defined at example.hs:7:0<br> instance (Num t1, Num t2) => FooOp t1 t2<br> -- Defined at
example.hs:10:0<br>Failed, modules loaded: none.</p>
<p>It seems that Num and Fractional are somewhat related. Any hint? Were my reasonings correct or was it only crap?</p>
<p>Thanks,</p>
<p>Cristiano</p></div>