<div dir="ltr">Hello !<div><br></div><div>I'm trying to write a polymorphic class instance in function of another class. So far it's pretty hard, I have trouble with headsizes and so on. I actually had to switch to type families pretty fast, and i'm still not out of the woods.</div><div><a href="http://lpaste.net/2650337298029215744" target="_blank">http://lpaste.net/<wbr>2650337298029215744</a><br></div><div><br></div><div>The context:</div><div>It concerns "tagless final interpreters" for EDSLs as described in <a href="http://okmij.org/ftp/tagless-final/course/lecture.pdf" target="_blank">http://okmij.org/ftp/<wbr>tagless-final/course/lecture.<wbr>pdf</a></div><div>A comparison with free monads: <a href="http://yowconference.com.au/slides/yowlambdajam2016/Hopkins-StopPayingForFreeMonads.pdf" target="_blank">http://yowconference.<wbr>com.au/slides/<wbr>yowlambdajam2016/Hopkins-<wbr>StopPayingForFreeMonads.pdf</a></div><div><br></div><div>The technique is fairly simple: instead of using ADTs to encode the operations of the language, we use haskell's class system. Languages become type classes, and interpreters are the instances which give various different meanings to the language expressions, which become simple haskell, ad-hoc polymorphic values.</div><div><br></div><div>It has strong advantages. For example, to write some expression in the composition of two languages (two typeclasses), you only need to combine the constraints:</div><div><font face="monospace, monospace">class WriteFile h m where</font></div><div><font face="monospace, monospace">  writeFile :: h -> String -> m ()</font></div><div><font face="monospace, monospace">class ReadFile h m where</font></div><div><font face="monospace, monospace">  readFile :: h -> m String<br></font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">-- we use Monad m to have access to monadic operations, but the language itself</font></div><div><font face="monospace, monospace">-- does not care that m be a monad or not.</font></div><div><font face="monospace, monospace">myExpression :: (WriteFile h m, ReadFile h m, Monad m) => h -> h -> String -> m String</font></div><div><font face="monospace, monospace">myExpression h h2 str = writeFile h str *> readFile h2</font></div><div><br></div><div>The fusion of both languages is utterly seamless, which is the beauty of typeclasses.</div><div><br></div><div>When writing DSLs, there are at least two basic operations that need to be done: combining DSLs to create richer languages (and as just shown, it's really simple with this technique), and translating one DSL into another.</div><div><br></div><div>That latter operation between DSLs is a bit more complicated with classes. I haven't found examples of how to do so in the wild (the technique doesn't seem very much used, esp compared with free monads), so if anybody knows how to do it or has references on that, it'd be much appreciated.</div><div><br></div><div>Theoretical example of what i'm trying to perform:</div><div><font face="monospace, monospace">class A a where</font></div><div><font face="monospace, monospace">  subtract :: a -> a -> a</font></div><div><font face="monospace, monospace">class B a where</font></div><div><font face="monospace, monospace">  add :: a -> a -> a</font></div><div><font face="monospace, monospace">  neg :: a -> a</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">-- interpreter from A to B: (doesn't work of course)</font></div><div><font face="monospace, monospace">class (B a) => A a where</font></div><div><font face="monospace, monospace">  subtract x y = add x (neg y)</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">-- interpreter for B in terms of Int:</font></div><div><font face="monospace, monospace">instance B Int where</font></div><div><font face="monospace, monospace">  add = (+)</font></div><div><font face="monospace, monospace">  neg = negate</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">expression :: (A a) => a</font></div><div><font face="monospace, monospace">expression = subtract (subtract 4 5) 6</font></div><div><font face="monospace, monospace">-- to get it to be interpreted, we need to select the type. usually we use a monomorphic version of id:</font></div><div><font face="monospace, monospace">asInt = identity :: Int -> Int</font></div><div><font face="monospace, monospace">intExp = asInt expression :: Int</font></div><div><br></div><div>For the instance B Int to be re-usable as instance/interpreter of (A Int) expressions, we need a way to write an interpretation of A a in terms of any pre-existing interpretation of B b.</div><div>Usually there are some issues, the need to wrap the types into newtype wrappers among others.</div><div><br></div><div>But in the case i'm trying to solve, it still doesn't work. To see where i'm stuck, see above my lpaste.</div><div><br></div><div>Any help or ideas would be very welcome! :) Thanks a lot in advance.</div></div>