<div dir="ltr"><div style>It seems to be my day to be the villain crying out against everything someone proposes. ;)</div><div><br></div>I for one would be strongly against a proposal that started actively deprecating the use of functional dependencies in user code. <div>
<br></div><div>There are a number of situations where the sheer amount of code you&#39;re forced to write by the mechanical translation to equalities and type families is explosive. I have dozens of classes of forms like</div>
<div><br></div><div><div>class Wrapped s t a b | a -&gt; s, b -&gt; t, a t -&gt; s, b s -&gt; t</div><div><br></div><div style>that become frankly nigh unusable after translation -- other examples are much worse. Similarly, in my experience, code written with monads-tf is almost always about 15% more verbose than code written with the mtl. It costs me two characters to put a space and an &#39;s&#39; in for MonadState s m, but I&#39;m usually not talking about the class unless I also reference the state type in the signature later, and there it saves me several characters per occurrence. When you have a more complicated functional dependency set like the above, though the difference isn&#39;t just a small constant amount of overhead. </div>
<div style><br></div><div style>It isn&#39;t that I use these things out of ignorance. I use them deliberately when the alternative isn&#39;t palatable.</div><div style><br></div><div style>I&#39;m all for internally desugaring everything into the same form internally, but Haskell as a culture has long embraced surface complexity so long as it can desugar to something internally reasonable: Consider, let vs. where, multiple definitions vs. case, layout vs. {}&#39;s, etc.</div>
</div><div style><br></div><div style>This is despite their varying surface complexities. Let is an expression and where being tied to the binding over statements, multiple bindings permitting you to shuffle backtrack pattern matches and guards across several arguments, etc. </div>
<div style><br></div><div style><div>Even changing out the implementation strategy for FDs in GHC has not been without pain, as it has resulted in changing the semantics of a number of corner cases. In fact, one could argue a large part of the stall in there being any progress in standardizing something in the TF vs FD vs both arena has been that it is very much a moving target. The document we would write today bears very little resemblance to the document we&#39;d have written 5 years ago. To standardize something you need to know what it means. If we&#39;d standardized FDs 5 years ago, we&#39;d be sitting here with an increasingly irrelevant language standard today or stuck in our tracks, and with very little practical impact on the community to show for it, and we&#39;d be likely rewriting the whole document now.<br>
</div></div><div style><br></div><div style>It takes a lot of effort to write up something like either TFs or FDs in their full generality, and the resulting document if based on just transcoding the state of GHC documents an implementation, not necessarily the most reasonable commonly agreeable semantics. </div>
<div style><br></div><div style>Arguably haskell-prime&#39;s role should be to follow far enough behind that the right decisions can be reached by implementations that blaze ahead of it. </div><div style><br></div><div style>
The only Haskell compiler even with type equalities is GHC at this point. They aren&#39;t a small change to introduce to a compiler. I for one work on a rather Haskelly compiler for my day job, when I&#39;m not crying gloom and doom on mailing lists, and have been actively considering how to go about it for almost two years now without losing the other properties that make that compiler special. I&#39;d really be a lot more comfortable having seen more success stories of people converting Haskell compilers over to this kind of approach before I felt it wise to say that this way that we know works and which has been productively in use in real code for ten years should be deprecated in favor of a way that only works with the single compiler.</div>
<div style><br></div><div style><div>A large part of the pain of &quot;choosing between&quot; FDs and TFs is that both have different somewhat overlapping strengths. In the end I don&#39;t think we will wind up choosing between them, we&#39;ll just desugar both to a common core. Hopefully after we have more than one point in the design space to choose from.</div>
</div><div style><br></div><div style><div><div>Removing a language feature just because another one can duplicate it with explosively more verbose code doesn&#39;t strike me as a very Haskelly way forward.<br></div><div>
<br></div></div></div><div style>-Edward</div><div><br></div></div><div class="gmail_extra"><br><br><div class="gmail_quote">On Tue, Apr 30, 2013 at 1:31 AM, AntC <span dir="ltr">&lt;<a href="mailto:anthony_clayden@clear.net.nz" target="_blank">anthony_clayden@clear.net.nz</a>&gt;</span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><br>
Now that the Type Equality coercions extension is stable and has been<br>
blessed by SPJ as &quot;a functional-dependency-like mechanism (but using<br>
equalities) for the result type&quot; [in<br>
<a href="http://hackage.haskell.org/trac/ghc/wiki/Records/OverloadedRecordFields#Hig" target="_blank">http://hackage.haskell.org/trac/ghc/wiki/Records/OverloadedRecordFields#Hig</a><br>
herranktypesandtypefunctions],<br>
no code is compelled to use FunDeps.<br>
<br>
[Note that this is orthogonal to the issue of overlaps. You can use the<br>
equalities mechanism happily with overlaps, see example below.<br>
 This is also orthogonal to type functions/families/associated types. You<br>
can use the equalities mechanism happily without type families.]<br>
<br>
There&#39;s plenty of code using FunDeps, so we couldn&#39;t just withdraw the<br>
extension. But we could start deprecating it.<br>
<br>
Better still, given that there is a mechanical way to convert FunDeps to<br>
equalities, we could start treating the FunDep on a class declaration as<br>
documentation, and validate that the instances observe the mechanical<br>
translation.<br>
<br>
Here&#39;s an example of the mechanical translation for HDeleteMany, the<br>
classic awkward example from HList.<br>
<br>
The problem is to delete all occurences of element type e in a HList l.<br>
Here&#39;s the &#39;non-solution&#39; naieve attempt from the HList paper:<br>
<br>
    class HDeleteMany e l l&#39; | e l -&gt; l&#39;<br>
    instance HDeleteMany a HNil HNil       -- base case OK<br>
    instance (HList l, HDeleteMany e l l&#39;)<br>
          =&gt; HDeleteMany e (HCons e l) l&#39;  -- element match, so omit<br>
    instance (HList l, HDeleteMany e l l&#39;)<br>
          =&gt; HDeleteMany e (HCons e&#39; l) (HCons e&#39; l&#39;)<br>
                                           -- element not match, so retain<br>
                                           -- + recurse on the tail<br>
<br>
&quot;The two overlapping instance heads for HCons are in no substitution<br>
ordering.&quot;<br>
<br>
Here&#39;s the mechanical translation, which _does_ compile:<br>
<br>
    class HDeleteMany e l l&#39;                -- | e l -&gt; l&#39;<br>
    instance (HNil ~ l&#39;)                    -- force the result<br>
          =&gt; HDeleteMany a HNil l&#39;          -- base case OK<br>
    instance (HList l, HDeleteMany e l l&#39;)<br>
          =&gt; HDeleteMany e (HCons e l) l&#39;   -- same as above<br>
    instance (HList l, HDeleteMany e l l&#39;&#39;, (HCons e&#39; l&#39;&#39;) ~ l&#39;)<br>
          =&gt; HDeleteMany e (HCons e&#39; l) l&#39;  -- force the result<br>
<br>
The translation rules (applying for the &#39;target&#39; term of a FunDep) are:<br>
1. If the target is a bare typevar not appearing otherwise in the head,<br>
   we&#39;re done OK.<br>
Otherwise:<br>
2. Replace the target with a fresh typevar.<br>
   (This forces instance match without inspecting the use site.)<br>
3. Add a type equality constraint<br>
   equating the fresh typevar with the as-was target term.<br>
   (This forces the result type, in the same way as a FunDep target.)<br>
<br>
<br>
Possible GSOC project?<br>
<br>
<br>
<br>
<br>
_______________________________________________<br>
Haskell-prime mailing list<br>
<a href="mailto:Haskell-prime@haskell.org">Haskell-prime@haskell.org</a><br>
<a href="http://www.haskell.org/mailman/listinfo/haskell-prime" target="_blank">http://www.haskell.org/mailman/listinfo/haskell-prime</a><br>
</blockquote></div><br></div>