[Haskell-cafe] Help with TH trick

David Feuer david.feuer at gmail.com
Sun Nov 6 21:50:11 UTC 2022


FYI, you can also hide the bijection with DerivingVia if you choose.

{-# language MultiParamTypeClasses, StandaloneDeriving, DerivingVia,
ScopedTypeVariables #-}

class Isomorphic plain fancy where
  to :: plain -> fancy
  from :: fancy -> plain

newtype Isomorphically plain fancy = Isomorphically fancy

instance (Eq plain, Isomorphic plain fancy) => Eq (Isomorphically plain
fancy) where
  Isomorphically x == Isomorphically y =
    from x == (from y :: plain)

data BoolLike = Trueish | Falseish

-- BL is not exported, so the bijection is hidden
newtype BL = BL BoolLike

instance Isomorphic Bool BL where
  to True = BL Trueish
  to False = BL Falseish

  from (BL Trueish) = True
  from (BL Falseish) = False

deriving via Isomorphically Bool BL
  instance Eq BoolLike

On Thu, Nov 3, 2022, 10:46 PM Michael Sloan <mgsloan at gmail.com> wrote:

> And the point of the "instantiators" is to support generating instances
> where you also require some value-level definition.  This can now be done
> via deriving via, except that deriving via will reveal implementation
> details to users which may be undesirable.  Very roughly (I did not look at
> how instantiators actually work in the package):
>
> data BoolLike = Trueish | Falseish
>
> $($(derive [d|
>   instance EqViaBijection Bool BoolLike where
>     to True = Trueish
>     to False = Falseish
>     from Trueish = True
>     from Falseish = False
> ]|))
>
> and the generated code would be an `Eq` instance, and the bijection used
> would not be visible to outside users (whereas it is if you use deriving
> via for this)
>
> On Thu, Nov 3, 2022 at 8:37 PM Michael Sloan <mgsloan at gmail.com> wrote:
>
>> Ah, it allows the mechanism to be open, so if you create a new typeclass
>> you can also declare how to use TH to derive instances.  Roughly (if TH
>> allowed splices in more spots in quotes):
>>
>> class Tangy a where
>>   -- ...
>>
>> instance Deriver (Tangy a) where
>>   runDeriver Proxy cxt ty = [d| instance $(cxt) => Tangy $(ty) where {-
>> ... -} |]
>>
>> Now in some other module I can do:
>>
>> $($(derive [d| instance Deriving (Tangy X) |]))
>>
>> The first splice will generate the code that dispatches to the instance,
>> and the next splice will generate the actual output.  This approach can
>> also allow you to use more complex dispatch to the deriver if you wish.
>> For example, maybe you have a Serializable class that comes with some
>> format type that is openly extensible.  You could define `instance
>> Deriver (Serializable Json a)` / `instance Deriver (Serializable Yaml a)`
>> etc and `derive` will dispatch to them when interpreting the outer splice.
>>
>> On Thu, Nov 3, 2022 at 8:23 PM David Feuer <david.feuer at gmail.com> wrote:
>>
>>> Why the double splice? What about this makes you generate code that when
>>> run will generate code?
>>>
>>> On Thu, Nov 3, 2022, 10:10 PM Michael Sloan <mgsloan at gmail.com> wrote:
>>>
>>>> Hmm, the documentation does list 5 bullets of why this is a nice way of
>>>> doing things.  But yes, considering this didn't catch on no doubt either
>>>> the docs are poor quality or the thing isn't really something people want.
>>>> FWIW I wrote this many years ago, before deriving via.  I just figured you
>>>> might be interested as it is very similar to your query.
>>>>
>>>> The examples could be better.  One thing to note is that you can list a
>>>> bunch of instances:
>>>>
>>>> $($(derive [d|
>>>>         instance Deriving (Storable X)
>>>>         instance Deriving (Eq X)
>>>>         instance Deriving (Show X)
>>>>         |]))
>>>>
>>>>
>>>> (purely an example, afaik it wouldn't make sense to use TH to generate
>>>> some canonical Eq and Show instances)
>>>>
>>>> Whereas with typical `$(mkStorable [t| Storable X|] $)` style you need
>>>> multiple splices and you need to know the names of these TH functions.
>>>>
>>>> In retrospect this would probably be better without the `Deriving`
>>>> wrapper, I think I needed that to disambiguate from the instantiators,
>>>> which indeed aren't properly documented at all (I don't recall how they
>>>> work, and don't feel like digging into it much further)
>>>>
>>>> On Tue, Oct 25, 2022 at 11:21 PM David Feuer <david.feuer at gmail.com>
>>>> wrote:
>>>>
>>>>> I must admit that looks rather mysterious to me. The documentation I
>>>>> saw doesn't make the benefit of the nested splice terribly obvious.
>>>>>
>>>>> On Tue, Oct 25, 2022, 10:53 PM Michael Sloan <mgsloan at gmail.com>
>>>>> wrote:
>>>>>
>>>>>> You might also be interested in
>>>>>> https://hackage.haskell.org/package/th-utilities-0.2.5.0/docs/TH-Derive.html -
>>>>>> though afaik it didn't catch on.
>>>>>>
>>>>>> The cleverness there is to use nesting splicing, where the inner one
>>>>>> generates code involving something like "runDeriver (Proxy @ Storable X)
>>>>>> ...", where runDeriver is a method of a typeclass.  The instances of this
>>>>>> class implement the code generation that is ultimately used for the output.
>>>>>>
>>>>>> $($(derive [d|
>>>>>>         instance Deriving (Storable X)
>>>>>>         |]))
>>>>>>
>>>>>>
>>>>>> On Sat, Oct 22, 2022, 13:48 David Feuer <david.feuer at gmail.com>
>>>>>> wrote:
>>>>>>
>>>>>>> Okay, I found it: recursion-schemes.
>>>>>>>
>>>>>>> On Sat, Oct 22, 2022, 3:05 PM David Feuer <david.feuer at gmail.com>
>>>>>>> wrote:
>>>>>>>
>>>>>>>> That doesn't work out so well when the class has a bunch of
>>>>>>>> methods. I definitely saw the nice way on Hackage ... somewhere.
>>>>>>>>
>>>>>>>> On Sat, Oct 22, 2022, 2:49 PM Brandon Allbery <allbery.b at gmail.com>
>>>>>>>> wrote:
>>>>>>>>
>>>>>>>>> I'm not aware of anything specifically like that, but the `makeā€¦`
>>>>>>>>> functions in
>>>>>>>>> https://hackage.haskell.org/package/deriving-compat-0.6.1/docs/Data-Deriving.html
>>>>>>>>> may allow you to do something similar?
>>>>>>>>>
>>>>>>>>> On Sat, Oct 22, 2022 at 2:32 PM David Feuer <david.feuer at gmail.com>
>>>>>>>>> wrote:
>>>>>>>>> >
>>>>>>>>> > I remember seeing a package that offered a function used
>>>>>>>>> something like this:
>>>>>>>>> >
>>>>>>>>> > $(deriveThingy [d| instance Foo a => Thingy a |])
>>>>>>>>> >
>>>>>>>>> > to allow the user to specify the constraint(s) for a generated
>>>>>>>>> instance. I'd love to borrow the code for that, but I can't remember what
>>>>>>>>> package it was in, what class it derived, etc. Can anyone help?
>>>>>>>>> > _______________________________________________
>>>>>>>>> > Haskell-Cafe mailing list
>>>>>>>>> > To (un)subscribe, modify options or view archives go to:
>>>>>>>>> > http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
>>>>>>>>> > Only members subscribed via the mailman list are allowed to post.
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> --
>>>>>>>>> brandon s allbery kf8nh
>>>>>>>>> allbery.b at gmail.com
>>>>>>>>>
>>>>>>>> _______________________________________________
>>>>>>> Haskell-Cafe mailing list
>>>>>>> To (un)subscribe, modify options or view archives go to:
>>>>>>> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
>>>>>>> Only members subscribed via the mailman list are allowed to post.
>>>>>>
>>>>>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/haskell-cafe/attachments/20221106/fa59cf24/attachment.html>


More information about the Haskell-Cafe mailing list