Czy jedyny „FlippyFloppyMorphism” jest „const mempty”?

Sep 25 2020

W Haskell mamy interesujący fakt, że każdy konstruktor typu f :: * -> *będący jednocześnie a Functori a Contravariantjest fantomem w swoim parametrze typu:

phantom :: (Functor f, Contravariant f) => f x -> f y

Innym sposobem wyrażenia tego jest to, że każdy konstruktor typu, który jest jednocześnie Functors i a, Contravariantjest naturalnie izomorficzny Const xdla niektórych x.

Oznacza to, że „jedyny” sposób (aż do izomorfizmu) na utworzenie instancji klasy:

class FlippyFloppyFunctor f
  where
  ffmap :: Either (y -> x) (x -> y) -> f x -> f y

tak, aby był zgodny z prawami funktora:

ffmap (Left id)       = id
ffmap (Right id)      = id
ffmap (Left  (g . f)) = ffmap (Left f)  . ffmap (Left g)
ffmap (Right (f . g)) = ffmap (Right f) . ffmap (Right g)

jest:

weirdmap :: Either (y -> x) (x -> y) -> Const r x -> Const r y
weirdmap = const $ \(Const x) -> Const x

tj modulo newtypes, const id.

Trudno mi zrozumieć, dlaczego jest to jedyna funkcja tego typu, która spełnia ograniczenia, chociaż potrafię zrozumieć różne nieformalne argumenty dotyczące absurd :: Void -> a/ discard :: a -> ()dlaczego istnienie takiej mapy implikuje, że funktor "jest fantomem" w swoim typie parametr.

Aby lepiej to zrozumieć, starałem się uprościć problem. Zamiast o tym myśleć FlippyFloppyFunctor, pomyślmy o:

class (Monoid a, Monoid b) => FlippyFloppyMorphism a b
  where
  ffmorph :: Either a a -> b

z analogicznymi prawami:

ffmorph (Left mempty)    = mempty
ffmorph (Right mempty)   = mempty
ffmorph (Left  (y <> x)) = ffmorph (Left x)  <> ffmorph (Left y)
ffmorph (Right (x <> y)) = ffmorph (Right x) <> ffmorph (Right y)

Zakładając, że ai b są non-przemienne monoids , jest to wciąż prawda, że tylko legalne realizacja FlippyFloppyMorphismjest const mempty? Czy nadal można wyjaśnić, dlaczego morfizm musi być „fantomem” w monoidach wejściowych, bez konieczności używania a Voidlub a ()do odniesienia?

Odpowiedzi

1 AsadSaeeduddin Sep 25 2020 at 14:43

Wydaje mi się, że odpowiedź w ogólnym przypadku brzmi „nie”, ponieważ monoidy mogą być przemienne.

Jeśli monoid jest przemienny, to Dual ajest tym samym monoidem co ai Either a ajest tym samym co a, a zatem degenerujemy się po prostu pytając, czy ffmorphjest to jedyny monoid homomorfizm a -> b. Odpowiedź brzmi nie".

Na przykład dla przemiennego monoidu dodawania mamy replicate 'a' :: Either (Sum Int) (Sum Int) -> String, gdzie:

replicateA (Left  0) = ""
replicateA (Right 0) = ""
replicateA (Left  (y + x)) = replicateA (Left x) ++ replicateA (Left y)
replicateA (Right (x + y)) = replicateA (Left x) ++ replicateA (Left y)

Jednak myślę, że może być tak, że dla monoidów _nie_komutacyjnych jedyną możliwą implementacją jest const mempty(na co nadal nie mam dowodów).