I was wrong. It's possible to write the general safe merge function in Haskell 98, by replacing the GADT with a record of functions. Sorry for the confusion!


On Aug 8, 2016 11:08 AM, "Edward Kmett" <ekmett@gmail.com> wrote:
You could always bury the GADT in an internal module with whatever super-general API you expose that uses it. ;)

Then I can have a sexy profunctor instance.

-Edward

On Mon, Aug 8, 2016 at 10:55 AM, Ryan Trinkle <ryan.trinkle@gmail.com> wrote:
Ah, that sounds interesting!  I'm not really sure there's much value to exposing the GADT, so perhaps it would be better to keep it abstract.  Plus, you can always open it up later, but doing a deprecation cycle if you decide it needs to be abstract will be super painful.  So i guess I'd tend to think that abstract is the safer option.

On Mon, Aug 8, 2016 at 10:28 AM, David Feuer <david.feuer@gmail.com> wrote:

Thanks! One more question: should I export the GADT constructors, or just bindings for them? The (theoretical) advantage of the latter is that I can enforce equivalences

dropC == mapFilterC (\_ _ -> Nothing)

and

preserveC == mapFilterC (\_ v -> Just v)

I think this lets us write

instance Functor f => Functor (MergeTactic f k v) where
  fmap _ Drop = Drop
  fmap f Preserve = MapFilter (\_ -> Just . f)
  fmap f (MapFilter p) = MapFilter (\k x -> f <$> p k x)
  fmap f (TraverseFilter p) = TraverseFilter (\k x -> fmap f <$> p k x)

and I think this lets profunctors write


instance Functor f => Profunctor (MergeTactic f k) where
  dimap _ _ Drop = Drop
  dimap f g Preserve = MapFilter (\_ -> Just . g . f)
  dimap f g (MapFilter p) = MapFilter (\k x -> g <$> p k (f x))
  dimap f g (TraverseFilter p) = TraverseFilter (\k x -> fmap g <$> p k (f x))


On Aug 8, 2016 9:59 AM, "Ryan Trinkle" <ryan.trinkle@gmail.com> wrote:
Wow, that looks great!  I wasn't expecting something that clean or that general.

The choice of INLINE or INLINABLE is above my pay grade, but I'm sure this will be a huge improvement either way!

On Mon, Aug 8, 2016 at 9:48 AM, David Feuer <david.feuer@gmail.com> wrote:

Is this good? https://github.com/haskell/containers/pull/317/files#diff-52bcc964836f9c44f05804a0c2267321R1904

Ryan: I worked out a general map combining function, with some help from Dan Doel and Cale, that I think gets most of what you'd want from such a thing without the horrors of mergeWithKey.

Both of you: I changed the Applicative actions to make sure they're entirely deterministic (going strictly in key order, interleaving actions associated with A-B, B-A, and A /\ B) without depending on tree balance. I decided that while it makes the most sense for the function to be in the middle, it's most convenient for it to be after the tactics, so I did that. If you disagree, let me know. Also, feedback on the type and constructor names would be great. Currently, the function is marked INLINE, like mergeWithKey. Should I mark it INLINABLE instead, and let users who want to be sure use an explicit `inline` call?