Search code examples
haskellgadt

Name a functor of functor


I have been playing around with updating Reflex to DMap 0.2 and ran into an issue translating one of the embedded functors.

Specifically it previously used a GADT to encode an a -> [WeakSubscriber a] relationship like so:

data FanSubscriberKey k a where
  FanSubscriberKey :: k a -> FanSubscriberKey k [WeakSubscriber a]

However in the newest version of DMap you can just embed a functor directly. I initially lifted the [] out of the above but realized that since I had a functor of a functor, I had a functor and wanted to eliminate the extra data all together. Unfortunately I cannot figure out a way to describe the above mapping without using a newtype. newtype WeakSubscriberList a = WeakSubscriberList [WeakSubscriber a] would solve the problem but would require wrapping and unwrapping the newtype.

Previous research points to this being considered a type-level lambda which is usually disallowed but the transformation here seemed simple enough that it could be possible, especially since I am not looking to define an instance or anything like that.

Using DMap 0.1 we can store a FanSubscriberKey k and have its value be a [WeakSubscriber a] in a DMap (FanSubscriberKey k) with its key being wrapped in the FanSubscriberKey constructor. In DMap 0.2 if I defined the above newtype I could similarly say DMap k WeakSubscriberList and get a similar result having an unwrapped key but a value wrapped in WeakSubscriberList. However what I would like to say is DMap k [WeakSubscriber] but that obviously won't work since [] is kind * -> * and WeakSubscriber is kind * -> *. If there were a type level . such that [] '. WeakSubscriber compiled that would do the trick but it similarly does not exist. I also tried a type alias but type WeakSubscriberList a = [WeakSubscriber a] requires that a be specified wherever WeakSubscriberList is used.


Solution

  • Data.Functor.Compose is a straightforward way of indirectly avoiding the newtype. It still involves a newtype, since that is how Compose is defined, but a new one doesn't need to be defined.

    The example above becomes DMap k (Compose [] WeakSubscriber).