<div dir="ltr"><div><br></div><div><br> > Hi Miao,<br> ><br> > interface Num {<br> >   Num (+)(Num a, Num b);<br> >   Num abs(Num a);<br> >   Num fromInteger(Integer);<br> >   ...<br> > }<br> ><br> > I'm trying to follow through this and I'm afraid I'm not seeing how your example OOP interface would work.<br> > What would the implementation of `AnyNum<Double>.abs` be?  It seems like it has no way of knowing whether the argument actually contains a Double or not?<br> ><br> > Wouldn't the `Num` interface instead need to be along the lines of the following?<br> ><br> > ```<br> > interface Num<T> {<br> >   T (+)(T a, T b);<br> >   T abs(T a);<br> >   T fromInteger(Integer);<br> > }<br> > ```<br> ><br> > or possibly<br> ><br> > ```<br> > interface Num<T> {<br> >   Num<T> (+)(Num<T> a, Num<T> b);<br> >   Num<T> abs(Num<T> a);<br> >   Num<T> fromInteger(Integer);<br> > }<br> > ```<br><br>Hi Aaron,<br><br>You are right, I was a bit rushing in the end, and I should write a proper example instead of pseudo code. Here is a<br>rewrite in actual C++: <a href="https://github.com/hellwolf/haskell-examples/blob/master/2022-05-25-inclusion-polymorphism/AnyNum.cpp">https://github.com/hellwolf/haskell-examples/blob/master/2022-05-25-inclusion-polymorphism/AnyNum.cpp</a><br><br>While re-writing, it's clear that you cannot fit Num class in OOP, in C++ it's rather using generics<br><br>```c++<br>// generics/compile-time ad-hoc "type classes"<br>// T: type of the number<br>// C: container of the number<br>template <typename T, typename C> class Num {<br>    typedef Num<T, C> ThisNum;<br><br>protected:<br>    int _val;<br><br>public:<br>    Num(int val) { _val = val; }<br><br>    // default implementation should work for int/double/etc.<br>public:<br>    static C add(const ThisNum& a, const ThisNum& b) { return C(a._val + b._val); }<br>    static C mul(const ThisNum& a, const ThisNum& b) { return C(a._val * b._val); }<br>    static C abs(const ThisNum& a) { return C(std::abs(a._val)); }<br>    static C negate(const ThisNum& a) { return C(std::negate(a._val)); }<br>    static C signum(const ThisNum& a) { return C(T(std::signbit(a._val))); }<br>    static C fromInteger(T x) { return C(x); }<br>};<br>```<br><br>This looks very much similar to the Haskell `Num` type class.<br><br>And the OOP-style stuff for Num and Show could rather be:<br><br>```c++<br>// run-time/inclusion polymorphism classes:<br>class INum {<br>public:<br>    virtual void operator +=(const INum&) = 0;<br>    virtual void operator *=(const INum&) = 0;<br>};<br>class IShow {<br>public:<br>    virtual std::string show() const = 0;<br>};<br>class IAnyNum: public INum, public IShow {};<br>```<br><br>So the question back to can we express the += and *= inclusion polymorphism for Haskell data types.<br><br>It seems to me that sub-typing libraries Olaf suggested could be something to look into.<br><br> ><br> > I'm thinking that the OOP version for your original Num interface would have the same unsafe casting issues that your haskell translation has.<br> > Or could you add an example of how `AnyNum<Double>.abs` would be implemented in the OOP version?<br><br>Yes, indeed, without using `Typeable`, there is unsafe casting issue. In the C++ version I fixed, it uses dynamic_cast,<br>which is basically using RTTI (similar to Haskell Typeable) to throw a run-time error if mismatched.<br><br>Also you were right, there is no sensible abs for the OOP version. The OOP Num should be "object-oriented" ones such as<br>"+=", "*=", etc.</div><div><br></div><div>Miao,<br></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Fri, May 27, 2022 at 4:20 AM Aaron VonderHaar <<a href="mailto:gruen0aermel@gmail.com" target="_blank">gruen0aermel@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div>Hi Miao,</div><div><br></div><div>> ```<br></div><div>> interface Num {<br>>   Num (+)(Num a, Num b);<br>>   Num abs(Num a);<br>>   Num fromInteger(Integer);</div><div>>   ...<br></div><div>> }</div><div>> ```<br></div><div><br></div><div>I'm trying to follow through this and I'm afraid I'm not seeing how your example OOP interface would work.</div><div>What would the implementation of `AnyNum<Double>.abs` be?  It seems like it has no way of knowing whether the argument actually contains a Double or not?<br></div><div><br></div><div>Wouldn't the `Num` interface instead need to be along the lines of the following?<br></div><div><br></div><div>```</div><div>interface Num<T> {</div><div>  T (+)(T a, T b);</div><div>  T abs(T a);</div><div>  T fromInteger(Integer);</div><div>}</div><div>```</div><div><br></div><div>or possibly</div><div><br></div><div><div>```</div><div>interface Num<T> {</div><div>  Num<T> (+)(Num<T> a, Num<T> b);</div><div>  Num<T> abs(Num<T> a);</div><div>  Num<T> fromInteger(Integer);</div><div>}</div><div>```</div></div><div><br></div><div>I'm thinking that the OOP version for your original Num interface would have the same unsafe casting issues that your haskell translation has.</div><div>Or could you add an example of how `AnyNum<Double>.abs` would be implemented in the OOP version?<br></div><div><br></div><div>--Aaron V.<br></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Thu, May 26, 2022 at 3:18 AM Miao ZhiCheng via Haskell-Cafe <<a href="mailto:haskell-cafe@haskell.org" target="_blank">haskell-cafe@haskell.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr">Hi Haskellers!<br><br>Here are some of my thoughts about polymorphism in Haskell especially the inclusion polymorphism, with questions in the<br>end.<br><br>~IGNORE THESE LHS BOILER PLATES~<br><br>> {-# LANGUAGE GADTs            #-}<br>> module Main where<br>> import Unsafe.Coerce ( unsafeCoerce )<br><br>~IGNORE THESE LHS BOILER PLATES~<br><br>* Classification of Polymorphism<br><br>Let's assume that we are following the classification of polymorphism where there are four types of it:<br><br>?? Btw, does anyone have a good citation of this classification? I got it from all over the Internet instead:<br>e.g <a href="https://www.tutorialspoint.com/types-of-polymorphisms-ad-hoc-inclusion-parametric-and-coercion" target="_blank">https://www.tutorialspoint.com/types-of-polymorphisms-ad-hoc-inclusion-parametric-and-coercion</a><br><br>** Overloading Polymorphism<br><br>It allows function with same name to act in different manner for different types.<br><br>** Coercion Polymorphism<br><br>Also called as casting sometimes.<br><br>** Inclusion Polymorphism<br><br>Also called as subtyping. This allows to point derived classes using base class pointers and references.<br><br>** Parametric Polymorphism<br><br>Also called early Binding, it allows to use same piece of code for different types. We can get it by using templates.<br><br>** Sub Classifications<br><br>In terms of being ad-hoc or universal:<br><br>- Overloading and Coercion are ad-hoc,<br>- Inclusion and Parametric are universal.<br><br>In terms of being compile-time vs run-time:<br><br>- Coercion, Overloading, Parametric are all compile-time polymorphism,<br>- while only Inclusion is run-time polymorphism.<br><br>* Different Polymorphism in Haskell<br><br>In terms of their counter parties in Haskell language:<br><br>** Overloading Polymorphismin Haskell<br><br>I think type classes basically provides such overloading ad-hoc polymorphism.<br><br>Some thinks so too: <a href="https://stackoverflow.com/questions/6636107/how-does-haskell-handle-overloading-polymorphism" target="_blank">https://stackoverflow.com/questions/6636107/how-does-haskell-handle-overloading-polymorphism</a><br><br>> class Fooable a where<br>>     foo :: a -> Int<br>> instance Fooable Int where<br>>     foo = id<br>> instance Fooable Bool where<br>>     foo _ = 42<br><br>** Coercion Polymorphism in Haskell<br><br>I think Haskell has both `Coercible` & `unsafeCoerce` for handling representational equality in compile time.<br><br>But I am not sure if c/c++ style of conercion between similar types such as int/float/double is something Haskell would<br>want to inject these magic conversion code ever.<br><br>** Parametric Polymorphism in Haskell<br><br>This is probably what Haskell is best at the most, demonstrated by the classic `map` function definition:<br><br>> map' :: (a -> b) -> [a] -> [b]<br>> map' _ []     = []<br>> map' f (x:xs) = f x : map' f xs<br><br>Note that, one could also combine parametric and overloading together using constraints:<br><br>> double :: Num a => a -> a<br>> double x = x + x<br><br>This `double` function is parametric, but constrained only to work with Num type classes, hence their implementations<br>are overloaded.<br><br>** Inclusion Polymorphism in Haskell<br><br>Ok, finally inclusion polymorphism is what I want to focus on and have some questions here.<br><br>Before that, I do want to mention one observation, inclusion polymorphism probably is most likely the old OOP habits<br>that die hard. For me at least, since I consider myself a recovering OOP run-time polymorphism addict, and my guess is<br>that unless the use case do need run-time polymorphism, e.g. run-time execution layer abstration, it is often better off<br>using other type of polymorphism to achieve the same result. And even Bjarne Stroustrup is flirting with the idea:<br>[[<a href="https://www.youtube.com/watch?v=xcpSLRpOMJM][Bjarne" target="_blank">https://www.youtube.com/watch?v=xcpSLRpOMJM][Bjarne</a> Stroustrup - Object Oriented Programming without Inheritance - ECOOP 2015]]<br><br>Let's think of translating this piece of pseudo OOP style code into haskell.<br><br>```<br>interface Num {<br>  Num (+)(Num a, Num b);<br>  Num (*)(Num a, Num b);<br>  Num abs(Num a);<br>  Num negate(Num a);<br>  Num signum(Num a);<br>  Num fromInteger(Integer);<br>}<br><br>interface Show {<br>  String show(Show a);<br>}<br><br>class AnyNum<T> implements Num, Show {<br>  // trivially implements Num Show interfaces<br>}<br><br>// Now we can use AnyNum<Int>, AnyNum<Double>, etc. through their Num or Show interfaces.<br>```<br><br>In Haskell though, thanks to the ability of having existential type, AnyNum is actually quite nice to write:<br><br>> data AnyNum where<br>>   MkAnyNum :: (Num a, Show a) => a -> AnyNum<br><br>But in order to use AnyNum like using interfaces in our pseudo code, we would have to write these boiler plates and in<br>some cases using unsafeCoerce due to not using Typeable run-time information:<br><br>> instance Num AnyNum where<br>>   (+) (MkAnyNum a) (MkAnyNum b) = MkAnyNum $ a + unsafeCoerce b<br>>   (*) (MkAnyNum a) (MkAnyNum b) = MkAnyNum $ a + unsafeCoerce b<br>>   abs (MkAnyNum a) = MkAnyNum . abs $ a<br>>   negate (MkAnyNum a) = MkAnyNum . negate $ a<br>>   signum (MkAnyNum a) = MkAnyNum . signum $ a<br>>   fromInteger = MkAnyNum . fromInteger<br>> instance Show AnyNum where<br>>   show (MkAnyNum a) = show a<br><br>Here is some test code that demonstrate how distressful unsafeCoerce is:<br><br>> main = do<br>>   let a = MkAnyNum(1 :: Int)<br>>   let b = MkAnyNum(2 :: Double)<br>>   let c = MkAnyNum(3 :: Int)<br>>   print $ show $ a + b<br>>   print $ show $ a + c<br><br>Outputs are:<br>```<br>λ><br>"4611686018427387905"<br>"4"<br>```<br><br>Few observations:<br><br>- If there is at most one occurance of the usage of the type class, there is no need for the unsafeCoerce.<br>- Otherwise, unsafeCoerce is required, or otherwise those functions could be left to "undefined".<br><br>So my central questions for discussions are:<br><br>- Is inclusion polymorphism something we should use at all in Haskell?<br>- These boiler plate code maybe better be generated using Haskell template instead, is there one already, or should<br>  there be one?<br><br><br>Cheers,<br>Miao</div>
_______________________________________________<br>
Haskell-Cafe mailing list<br>
To (un)subscribe, modify options or view archives go to:<br>
<a href="http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe" rel="noreferrer" target="_blank">http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe</a><br>
Only members subscribed via the mailman list are allowed to post.</blockquote></div>
</blockquote></div>