<div dir="ltr">Hi everyone,<div><br></div><div>IIRC one of the arguments against having many separate classes is that a class is not a just set of methods, it's also the relations between them, such as the important laws between `return` and `>>=`. And then for example a class with just `return` doesn't give any information what `return x` means or what should be its properties.</div><div><br></div><div>That said, one of really painful points of Haskell is that refactoring a hierarchy of type-classes means breaking all the code that implements them. This was also one of the main reasons why reason making Applicative a superclass of Monad took so long. It'd be much nicer to design type-classes in such a way that an implementation doesn't have to really care about the exact hierarchy.</div><div><br></div><div>The Go language takes a very simple view on this: A type implements an interface if all the methods are implemented, without having to explicitly specify this intent [1]. This looks very nice and clean indeed. But the drawback is that this further decouples type-classes (interfaces) from their laws (like monad laws, monoid laws etc.). For example, in Haskell we could have</div><div><br></div><div>class (Return m, Bind m) => Monad m where</div><div><br></div><div>without any methods specified. But instances of `Monad` should be only such types for which `return` and `>>=` satisfy the monad laws. And this would distinguish them from types that have both `Return` and `Bind` instances, but don't satisfy the laws.</div><div><br></div><div>Unfortunately I'm not sure if there is a good solution for achieving both these directions.</div><div><br></div><div>[1] <a href="https://tour.golang.org/methods/10">https://tour.golang.org/methods/10</a></div><div><br></div><div>Cheers,</div><div>Petr</div><div><br><div class="gmail_quote"><div dir="ltr">čt 4. 10. 2018 v 3:56 odesílatel Anthony Clayden <<a href="mailto:anthony_clayden@clear.net.nz">anthony_clayden@clear.net.nz</a>> napsal:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="auto"><span style="white-space:pre-wrap;background-color:rgb(255,255,255)">> We are adding classes and instances to Helium.</span><br></div><div dir="auto"><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)">> We wondered about the aspect that it is allowed to have a class instance</pre></div><div dir="auto"><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)">> of which not all fields have a piece of code/value associated with them, ...</pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)"><br></pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)">I have a suggestion for that. But first let me understand where you're going with Helium. Are you aiming to slavishly reproduce Haskell's classes/instances, or is this a chance for a rethink?</pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)"><br></pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)">Will you want to include associated types and associated datatypes in the classes? Note those are just syntactic sugar for top-level type families and data families. It does aid readability to put them within the class.</pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)"><br></pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)">I would certainly rethink the current grouping of methods into classes. Number purists have long wanted to split class Num into Additive vs Multiplicative. (Additive would be a superclass of Multiplicative.) For the Naturals perhaps we want Presburger arithmetic then Additive just contains (+), with `negate` certainly in a different class, perhaps (-) subtract also in a dedicated class. Also there's people wanting Monads with just `bind` not `return`. But restructuring the Prelude classes/methods is just too hard with all that legacy code. Even though you should be able to do:</pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)"><br></pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)">class (Additive a, Subtractive a, Negative a, Multiplicative a, Divisive a) => Num a</pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)"><br></pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)">Note there's a lot of classes with a single method, and that seems to be an increasing trend. Historically it wasn't so easy in Haskell to do that superclass constraints business; if it had been perhaps there would be more classes with a single method. Then there's some disadvantages to classes holding multiple methods:</pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)">* the need to provide an overloading for every method, even though it may not make sense</pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)"> (or suffer a run-time error, as you say)</pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)">* the inability to 'fine tune' methods for a specific datatype [**]</pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)">* an internal compiler/object code cost of passing a group of methods in a dictionary as tuple</pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)"> (as apposed to directly selecting a single method)</pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)"><br></pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)">[**] Nats vs Integrals vs Fractionals for `Num`; and (this will be controversial, but ...) Some people want to/some languages do use (+) for concatenating Strings/lists. But the other methods in `Num` don't make any sense.</pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)"><br></pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)">If all your classes have a single method, the class name would seem to be superfluous, and the class/instance decl syntax seems too verbose.</pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)"><br></pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)">So here's a suggestion. I'll need to illustrate with some definite syntax, but there's nothing necessary about it. (I'll borrow the Explicit Type Application `@`.) To give an instance overloading for method `show` or (==)</pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)"><br></pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)">show @Int = primShowInt -- in effect pattern matching on the type</pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)">(==) @Int = primEqInt -- so see showList below</pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)">That is: I'm giving an overloading for those methods on type `Int`. How do I declare those methods are overloadable? In their signature:</pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)"><br></pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)">show @a :: a -> String -- compare show :: Show a => a -> String</pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)">(==) @a :: a -> a -> Bool</pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)">Non-overladable functions don't have `@a` to the left of `::`.</pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)">How do I show that a class has a superclass constraint? That is: a method has a supermethod constraint, we'll still use `=>`:</pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)"><br></pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)">show @a :: showsPrec @a => a -> String -- supermethod constraint</pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)">show @[a] :: show a => [a] -> String -- instance decl, because not bare a, with constraint =></pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)">show @[a] xss = showList xss</pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)">(*) @a :: (+) @a => a -> a -> a</pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)"><br></pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)">Is this idea completely off the wall? Take a look at Wadler's original 1988 memo introducing what became type classes. <div><a href="http://homepages.inf.ed.ac.uk/wadler/papers/class-letter/class-letter.txt" target="_blank">http://homepages.inf.ed.ac.uk/wadler/papers/class-letter/class-letter.txt</a></div></pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)"><br></pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)">It reviews several possible designs, but not all those possibilities made it into his paper (with Stephen Blott) later in 1988/January 1989. In particular look at Section 1's 'Simple overloading'. It's what I'm suggesting above (modulo a bit of syntax). At the end of Section 1, Wadler rejects this design because of "potential blow-ups". But he should have pushed the idea a bit further. Perhaps he was scared to allow function/method names into type signatures? (I've already sneaked that in above with constraints.) These days Haskell is getting more relaxed about namespaces: the type `@`pplication exactly allows type names appearing in terms. So to counter his example, the programmer writes:</pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)"><br></pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)">square x = x * x -- no explicit signature given</pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)">square :: (*) @a => a -> a -- signature inferred, because (*) is overloaded</pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)">rms = sqrt . square -- no explicit signature</pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)">rms :: sqrt @a => a -> a -- signature inferred</pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)"><br></pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)">Note the inferred signature for `rms` doesn't need `(*) @a` even though it's inferred from `square`. Because (*) is a supermethod of `sqrt`. `sqrt` might also have other supermethods, that amount to `Floating`.</pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)"><br></pre><pre style="white-space:pre-wrap;background-color:rgb(255,255,255)">> ... a run-time error results.
>
> Does anyone know of a rationale for this choice, since it seems rather unhaskell-like.
</pre><div dir="auto"><br></div><div dir="auto">If you allow default method implementations (in the class, as Cale points out), then I guess you have to allow instance decls that don't mention all the methods. I think there should at least be a warning if there's no default method. Also beware the default method might have a more specific signature, which means it can't be applied for some particular instance.</div><div dir="auto"><br></div><div dir="auto">Altogether, I'd say, the culprit is the strong bias in early Haskell to bunch methods together into classes. These days with Haskell's richer/more fine-tuned typeclass features: what do typeclasses do that can't be done more precisely at method level -- indeed that would _better_ be done at method level?</div><div dir="auto"><br></div><div dir="auto"><br></div><div dir="auto">AntC</div></div>
_______________________________________________<br>
Haskell-prime mailing list<br>
<a href="mailto:Haskell-prime@haskell.org" target="_blank">Haskell-prime@haskell.org</a><br>
<a href="http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-prime" rel="noreferrer" target="_blank">http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-prime</a><br>
</blockquote></div></div></div>