<div dir="ltr"><div dir="ltr">Using generics and default worked brilliantly, thanks! I didn't use higgledy, so let me know if I missed an opportunity for something. I just wanted to post my solution here. My only questions would be: isn't there a library on hackage that already does this?</div><div dir="ltr"><br></div><div dir="ltr">However, anyone reading who just want to learn how to use generics, I recommend reading <a href="http://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#generic-programming">http://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#generic-programming</a>, as that's basically all I needed to learn how to do this. (Nathan's link pointed me there, again: thanks!)</div><div dir="ltr"><br></div><div dir="ltr">For getting everything to work, I added <span style="background-color:rgb(32,32,32);color:rgb(192,192,192);font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;white-space:pre">{-# </span><span style="background-color:rgb(32,32,32);font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;white-space:pre;color:rgb(247,140,108)">LANGUAGE</span><span style="background-color:rgb(32,32,32);color:rgb(192,192,192);font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;white-space:pre"> TypeOperators, DefaultSignatures #-}</span> (GHC is great at telling me what to enable).</div><div dir="ltr"><br></div><div dir="ltr">I was able to write a generic version for removing whitespace, which essentially is an fmap. To derive an instance, I can indeed just write:<div><div style="color:rgb(192,192,192);background-color:rgb(32,32,32);font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;line-height:18px;white-space:pre"><div><span style="color:rgb(247,140,108)">instance</span> WhiteSpaced ClassItem <span style="color:rgb(247,140,108)">where</span></div><div><span style="color:rgb(247,140,108)"></span></div></div><div><br></div><div>The most straightforward thing to do, is to write a generic GWhiteSpaced class and then the WhiteSpaced class. Here is what I wrote:</div><div><div><div style="color:rgb(192,192,192);background-color:rgb(32,32,32);font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;line-height:18px;white-space:pre"><div><span style="color:rgb(247,140,108)">class</span> GWhiteSpaced <span style="color:rgb(51,119,119)">f</span> <span style="color:rgb(247,140,108)">where</span></div><div> <span style="color:rgb(130,170,255)">gremoveWS</span> <span style="color:rgb(247,140,108)">::</span> <span style="color:rgb(51,119,119)">f</span> <span style="color:rgb(51,119,119)">a</span> <span style="color:rgb(247,140,108)">-></span> <span style="color:rgb(51,119,119)">f</span> <span style="color:rgb(51,119,119)">a</span> <span style="color:rgb(137,221,255);font-weight:bold">--</span><span style="color:rgb(84,110,122);font-weight:bold"> generic version of removeWS</span></div><br><div><span style="color:rgb(247,140,108)">instance</span> GWhiteSpaced U1 <span style="color:rgb(247,140,108)">where</span></div><div> gremoveWS U1 <span style="color:rgb(199,146,234)">=</span> U1</div><div><span style="color:rgb(247,140,108)">instance</span> (GWhiteSpaced <span style="color:rgb(51,119,119)">a</span>, GWhiteSpaced <span style="color:rgb(51,119,119)">b</span>) <span style="color:rgb(247,140,108)">=></span> GWhiteSpaced (<span style="color:rgb(51,119,119)">a</span> :*: <span style="color:rgb(51,119,119)">b</span>) <span style="color:rgb(247,140,108)">where</span></div><div> gremoveWS (x <span style="color:rgb(199,146,234)">:*:</span> y) <span style="color:rgb(199,146,234)">=</span> gremoveWS x <span style="color:rgb(199,146,234)">:*:</span> gremoveWS y</div><div><span style="color:rgb(247,140,108)">instance</span> (GWhiteSpaced <span style="color:rgb(51,119,119)">a</span>, GWhiteSpaced <span style="color:rgb(51,119,119)">b</span>) <span style="color:rgb(247,140,108)">=></span> GWhiteSpaced (<span style="color:rgb(51,119,119)">a</span> :+: <span style="color:rgb(51,119,119)">b</span>) <span style="color:rgb(247,140,108)">where</span></div><div> gremoveWS (L1 x) <span style="color:rgb(199,146,234)">=</span> L1 <span style="color:rgb(199,146,234)">$</span> gremoveWS x</div><div> gremoveWS (R1 x) <span style="color:rgb(199,146,234)">=</span> R1 <span style="color:rgb(199,146,234)">$</span> gremoveWS x</div><div><span style="color:rgb(247,140,108)">instance</span> (GWhiteSpaced <span style="color:rgb(51,119,119)">a</span>) <span style="color:rgb(247,140,108)">=></span> GWhiteSpaced (M1 <span style="color:rgb(51,119,119)">i</span> <span style="color:rgb(51,119,119)">c</span> <span style="color:rgb(51,119,119)">a</span>) <span style="color:rgb(247,140,108)">where</span></div><div> gremoveWS (M1 x) <span style="color:rgb(199,146,234)">=</span> M1 <span style="color:rgb(199,146,234)">$</span> gremoveWS x</div><div><span style="color:rgb(247,140,108)">instance</span> (WhiteSpaced <span style="color:rgb(51,119,119)">a</span>) <span style="color:rgb(247,140,108)">=></span> GWhiteSpaced (K1 <span style="color:rgb(51,119,119)">i</span> <span style="color:rgb(51,119,119)">a</span>) <span style="color:rgb(247,140,108)">where</span></div><div> gremoveWS (K1 x) <span style="color:rgb(199,146,234)">=</span> K1 <span style="color:rgb(199,146,234)">$</span> removeWS x</div><br><div><span style="color:rgb(247,140,108)">class</span> WhiteSpaced <span style="color:rgb(51,119,119)">a</span> <span style="color:rgb(247,140,108)">where</span></div><div> <span style="color:rgb(130,170,255)">removeWS</span> <span style="color:rgb(247,140,108)">::</span> <span style="color:rgb(51,119,119)">a</span> <span style="color:rgb(247,140,108)">-></span> <span style="color:rgb(51,119,119)">a</span> <span style="color:rgb(137,221,255);font-weight:bold">--</span><span style="color:rgb(84,110,122);font-weight:bold"> remove as much whitespace as possible without changing semantics</span></div><div> <span style="color:rgb(247,140,108)">default</span> removeWS <span style="color:rgb(247,140,108)">::</span> (GWhiteSpaced (Rep <span style="color:rgb(51,119,119)">a</span>), Generic <span style="color:rgb(51,119,119)">a</span>) <span style="color:rgb(247,140,108)">=></span> <span style="color:rgb(51,119,119)">a</span> <span style="color:rgb(247,140,108)">-></span> <span style="color:rgb(51,119,119)">a</span></div><div> removeWS <span style="color:rgb(199,146,234)">=</span> GHC<span style="color:rgb(199,146,234)">.</span>Generics<span style="color:rgb(199,146,234)">.</span>to <span style="color:rgb(199,146,234)">.</span> gremoveWS <span style="color:rgb(199,146,234)">.</span> from</div><br></div></div></div></div></div><div><br></div><div>This really helps generalize things in those cases where the data-structure changes, but the WhiteSpaced class is not the only class that follows this pattern. Naturally, I would like to:</div><div>- avoid repeating the generic class for each fmap-like class (renaming method-names, simplifying expressions, etc, are all very similar functions).</div><div>- and ideally remove the circular dependency, so I can put the generic classes into separate modules without getting orphaned instances for the specific ones.<br></div><div><br></div><div>I managed to do both with a single solution. First of all, I will use a phantom type to keep track of which instance to use. If you haven't seen a phantom type: it's just a convenient way of binding type variables and passing those around. I'd love to use the default one in the Prelude / RIO, but I can never find it and getting an extra dependency is not worth it, so I always end up defining one:</div><div><div style="color:rgb(192,192,192);background-color:rgb(32,32,32);font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;line-height:18px;white-space:pre"><div><span style="color:rgb(247,140,108)">data</span> Phantom <span style="color:rgb(51,119,119)">k</span> <span style="color:rgb(247,140,108)">=</span> Phantom</div></div></div><div>I'll use this phantom type later when I create an empty datatype (as empty as they get) whose only purpose is to denote that I'm using the whitespace function. Using Phantom types, I can unambiguously define:</div><div><div style="color:rgb(192,192,192);background-color:rgb(32,32,32);font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;line-height:18px;white-space:pre"><div><span style="color:rgb(247,140,108)">class</span> FmapLike <span style="color:rgb(51,119,119)">k</span> <span style="color:rgb(51,119,119)">f</span> <span style="color:rgb(247,140,108)">where</span></div><div> <span style="color:rgb(130,170,255)">gmap</span> <span style="color:rgb(247,140,108)">::</span> Phantom <span style="color:rgb(51,119,119)">k</span> <span style="color:rgb(247,140,108)">-></span> <span style="color:rgb(51,119,119)">f</span> <span style="color:rgb(51,119,119)">a</span> <span style="color:rgb(247,140,108)">-></span> <span style="color:rgb(51,119,119)">f</span> <span style="color:rgb(51,119,119)">a</span></div><div><span style="color:rgb(247,140,108)">class</span> FmapInstance <span style="color:rgb(51,119,119)">k</span> <span style="color:rgb(51,119,119)">a</span> <span style="color:rgb(247,140,108)">where</span></div><div> <span style="color:rgb(130,170,255)">gmapinstance</span> <span style="color:rgb(247,140,108)">::</span> Phantom <span style="color:rgb(51,119,119)">k</span> <span style="color:rgb(247,140,108)">-></span> <span style="color:rgb(51,119,119)">a</span> <span style="color:rgb(247,140,108)">-></span> <span style="color:rgb(51,119,119)">a</span></div></div></div><div>Note that the *only* purpose of the type variable k here, is to enable reuse: by filling in different values for k, I can instantiate whitespaces and other fmap-like functions in the same way. Otherwise, FmapLike and FmapInstance just mimick GWhiteSpaced and WhiteSpaced respectively.</div><div><br></div><div>My generic FmapLike function is nearly the same as my generic whitespace function, the only thing I add is passing the type variable around:</div><div><div style="color:rgb(192,192,192);background-color:rgb(32,32,32);font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;line-height:18px;white-space:pre"><div><span style="color:rgb(247,140,108)">instance</span> FmapLike <span style="color:rgb(51,119,119)">x</span> U1 <span style="color:rgb(247,140,108)">where</span></div><div> gmap _ U1 <span style="color:rgb(199,146,234)">=</span> U1</div><div><span style="color:rgb(247,140,108)">instance</span> (FmapLike <span style="color:rgb(51,119,119)">x</span> <span style="color:rgb(51,119,119)">a</span>, FmapLike <span style="color:rgb(51,119,119)">x</span> <span style="color:rgb(51,119,119)">b</span>) <span style="color:rgb(247,140,108)">=></span> FmapLike <span style="color:rgb(51,119,119)">x</span> (<span style="color:rgb(51,119,119)">a</span> :*: <span style="color:rgb(51,119,119)">b</span>) <span style="color:rgb(247,140,108)">where</span></div><div> gmap f (x <span style="color:rgb(199,146,234)">:*:</span> y) <span style="color:rgb(199,146,234)">=</span> gmap f x <span style="color:rgb(199,146,234)">:*:</span> gmap f y</div><div><span style="color:rgb(247,140,108)">instance</span> (FmapLike <span style="color:rgb(51,119,119)">x</span> <span style="color:rgb(51,119,119)">a</span>, FmapLike <span style="color:rgb(51,119,119)">x</span> <span style="color:rgb(51,119,119)">b</span>) <span style="color:rgb(247,140,108)">=></span> FmapLike <span style="color:rgb(51,119,119)">x</span> (<span style="color:rgb(51,119,119)">a</span> :+: <span style="color:rgb(51,119,119)">b</span>) <span style="color:rgb(247,140,108)">where</span></div><div> gmap f (L1 x) <span style="color:rgb(199,146,234)">=</span> L1 <span style="color:rgb(199,146,234)">$</span> gmap f x</div><div> gmap f (R1 x) <span style="color:rgb(199,146,234)">=</span> R1 <span style="color:rgb(199,146,234)">$</span> gmap f x</div><div><span style="color:rgb(247,140,108)">instance</span> (FmapLike <span style="color:rgb(51,119,119)">x</span> <span style="color:rgb(51,119,119)">a</span>) <span style="color:rgb(247,140,108)">=></span> FmapLike <span style="color:rgb(51,119,119)">x</span> (M1 <span style="color:rgb(51,119,119)">i</span> <span style="color:rgb(51,119,119)">c</span> <span style="color:rgb(51,119,119)">a</span>) <span style="color:rgb(247,140,108)">where</span></div><div> gmap f (M1 x) <span style="color:rgb(199,146,234)">=</span> M1 <span style="color:rgb(199,146,234)">$</span> gmap f x</div><div><span style="color:rgb(247,140,108)">instance</span> FmapInstance <span style="color:rgb(51,119,119)">x</span> <span style="color:rgb(51,119,119)">a</span> <span style="color:rgb(247,140,108)">=></span> FmapLike <span style="color:rgb(51,119,119)">x</span> (K1 <span style="color:rgb(51,119,119)">i</span> <span style="color:rgb(51,119,119)">a</span>) <span style="color:rgb(247,140,108)">where</span></div><div> gmap f (K1 x) <span style="color:rgb(199,146,234)">=</span> K1 <span style="color:rgb(199,146,234)">$</span> gmapinstance f x</div></div></div><div><br></div><div>Now to define the WhiteSpace class as before, I just need six lines. Furthermore, these six lines can be in a separate file without creating orphaned instances:</div><div><div style="color:rgb(192,192,192);background-color:rgb(32,32,32);font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;line-height:18px;white-space:pre"><div><span style="color:rgb(247,140,108)">data</span> WS</div><div><span style="color:rgb(247,140,108)">class</span> WhiteSpaced <span style="color:rgb(51,119,119)">a</span> <span style="color:rgb(247,140,108)">where</span></div><div> <span style="color:rgb(130,170,255)">removeWS</span> <span style="color:rgb(247,140,108)">::</span> <span style="color:rgb(51,119,119)">a</span> <span style="color:rgb(247,140,108)">-></span> <span style="color:rgb(51,119,119)">a</span> <span style="color:rgb(137,221,255);font-weight:bold">--</span><span style="color:rgb(84,110,122);font-weight:bold"> remove as much whitespace as possible without changing semantics</span></div><div> <span style="color:rgb(247,140,108)">default</span> removeWS <span style="color:rgb(247,140,108)">::</span> (FmapLike WS (Rep <span style="color:rgb(51,119,119)">a</span>), Generic <span style="color:rgb(51,119,119)">a</span>) <span style="color:rgb(247,140,108)">=></span> <span style="color:rgb(51,119,119)">a</span> <span style="color:rgb(247,140,108)">-></span> <span style="color:rgb(51,119,119)">a</span></div><div> removeWS <span style="color:rgb(199,146,234)">=</span> GHC<span style="color:rgb(199,146,234)">.</span>Generics<span style="color:rgb(199,146,234)">.</span>to <span style="color:rgb(199,146,234)">.</span> gmap (Phantom<span style="color:rgb(247,140,108)">::</span>Phantom WS) <span style="color:rgb(199,146,234)">.</span> from</div><div><span style="color:rgb(247,140,108)">instance</span> WhiteSpaced <span style="color:rgb(51,119,119)">a</span> <span style="color:rgb(247,140,108)">=></span> FmapInstance WS <span style="color:rgb(51,119,119)">a</span> <span style="color:rgb(247,140,108)">where</span></div><div> gmapinstance _ <span style="color:rgb(199,146,234)">=</span> removeWS</div><div></div></div></div><div>(The instance is not an orphan because WS is defined here)</div><div><br></div><div>Note that instead of WS, I could use any other datatype token, it doesn't have to be an empty datatype. I'm just defining it as an empty datatype to make it absolutely clear that it's not storing any data. It also provides a good place to document what WS is actually intended for. As a final touch, I'm defining:</div><div>gmapGeneric = (\x -> GHC<span style="color:rgb(199,146,234)">.</span>Generics<span style="color:rgb(199,146,234)">.</span>to <span style="color:rgb(199,146,234)">.</span> gmap x <span style="color:rgb(199,146,234)">.</span> from)</div><div>(this shortens removeWS a little)</div><div><br></div><div>So final question: is there any library that implements gmapGeneric or gmap?</div><div><br></div><div>Also a big thanks to everyone who helped put Generics in Haskell. I've seen helpful error messages and good documentation all the way through!</div><div><br></div>Best,<div><br></div><div>Sebastiaan</div><div><br></div><div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Thu, Oct 3, 2019 at 6:41 PM Yuji Yamamoto <<a href="mailto:whosekiteneverfly@gmail.com">whosekiteneverfly@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">Use DeriveGenerics, and <a href="https://github.com/i-am-tom/higgledy" target="_blank">higgledy</a> (or some packages supporting higher kinded data) would help you.<br></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">2019年10月4日(金) 5:56 Sebastiaan Joosten <<a href="mailto:sjcjoosten%2Bhaskelcafe@gmail.com" target="_blank">sjcjoosten+haskelcafe@gmail.com</a>>:<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"><span style="color:rgb(0,0,0);font-family:Helvetica;font-size:12px">Hi all,</span><div style="color:rgb(0,0,0);font-family:Helvetica;font-size:12px"><br></div><div style="color:rgb(0,0,0);font-family:Helvetica;font-size:12px">I'm writing a lot of code that looks like this:</div><div style="color:rgb(0,0,0);font-family:Helvetica;font-size:12px"><div style="color:rgb(192,192,192);background-color:rgb(32,32,32);font-family:Menlo,Monaco,"Courier New",monospace;line-height:18px;white-space:pre-wrap"><div><span style="color:rgb(247,140,108)">instance</span> WhiteSpaced ClassItem <span style="color:rgb(247,140,108)">where</span></div><div> removeWS (Method a b c) <span style="color:rgb(199,146,234)">=</span> Method (removeWS a) (removeWS b) (removeWS c)</div><div> removeWS (Declaration b) <span style="color:rgb(199,146,234)">=</span> Declaration (removeWS b)</div></div></div><div style="color:rgb(0,0,0);font-family:Helvetica;font-size:12px"><br></div><div style="color:rgb(0,0,0);font-family:Helvetica;font-size:12px">Typically, all the way at the end there's an instance that deviates (sometimes the deviating instances are somewhere in the middle). I need to do this for a lot of functions, and a lot of data types, and all I'm doing here is rewriting the data-type declaration in a different syntax (except that you do not know the types of a, b and c from the above). For the sake of maintainability, I want to avoid this code-duplication and focus only on the deviating instances.</div><div style="color:rgb(0,0,0);font-family:Helvetica;font-size:12px"><br></div><div style="color:rgb(0,0,0);font-family:Helvetica;font-size:12px">How to do better? I don't see how to use generics (in the hope of only writing 'instance WhiteSpaced ClassItem where' instead of the three lines above) for this: the types for a, b and c are all different here. Would this be easier with Template Haskell? (in the hope of only writing $(''something ClassItem) instead of the three lines above)</div><div style="color:rgb(0,0,0);font-family:Helvetica;font-size:12px"><br></div><div style="color:rgb(0,0,0);font-family:Helvetica;font-size:12px"><div>My main concern is maintainability, an ideal solution is either a clear one-liner or a library import (in the same way that aeson allows me to use generics or Template Haskell without needing to know much about them). Other solutions are welcome too.</div></div><div style="color:rgb(0,0,0);font-family:Helvetica;font-size:12px"><br></div><div style="color:rgb(0,0,0);font-family:Helvetica;font-size:12px">Best,</div><div style="color:rgb(0,0,0);font-family:Helvetica;font-size:12px"><br></div><div style="color:rgb(0,0,0);font-family:Helvetica;font-size:12px">Sebastiaan</div></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><br clear="all"><br>-- <br><div dir="ltr"><div dir="ltr"><div><div dir="ltr"><div><div dir="ltr"><div>山本悠滋<br>twitter: <a href="https://twitter.com/igrep" target="_blank">https://twitter.com/igrep</a><br>GitHub: <a href="https://github.com/igrep" target="_blank">https://github.com/igrep</a></div><div>GitLab: <a href="https://gitlab.com/igrep" target="_blank">https://gitlab.com/igrep</a><br>Facebook: <a href="http://www.facebook.com/igrep" target="_blank">http://www.facebook.com/igrep</a></div></div></div></div></div></div></div>
</blockquote></div></div></div>