Das Applicative
Typklasse repräsentiert laxe monoidale Funktoren, die die kartesische monoidale Struktur in der Kategorie der typisierten Funktionen beibehalten.
Mit anderen Worten, angesichts der kanonischen Isomorphismen, (,)
die eine monoidale Struktur bilden:
-- Implementations left to the motivated reader
assoc_fwd :: ((a, b), c) -> (a, (b, c))
assoc_bwd :: (a, (b, c)) -> ((a, b), c)
lunit_fwd :: ((), a) -> a
lunit_bwd :: a -> ((), a)
runit_fwd :: (a, ()) -> a
runit_bwd :: a -> (a, ())
Die Typklasse und ihre Gesetze können äquivalent so geschrieben werden:
class Functor f => Applicative f
where
zip :: (f a, f b) -> f (a, b)
husk :: () -> f ()
-- Laws:
-- assoc_fwd >>> bimap id zip >>> zip
-- =
-- bimap zip id >>> zip >>> fmap assoc_fwd
-- lunit_fwd
-- =
-- bimap husk id >>> zip >>> fmap lunit_fwd
-- runit_fwd
-- =
-- bimap id husk >>> zip >>> fmap runit_fwd
Man könnte sich fragen, wie ein Funktor aussehen könnte, der in Bezug auf dieselbe Struktur oplax monoidal ist:
class Functor f => OpApplicative f
where
unzip :: f (a, b) -> (f a, f b)
unhusk :: f () -> ()
-- Laws:
-- assoc_bwd <<< bimap id unzip <<< unzip
-- =
-- bimap unzip id <<< unzip <<< fmap assoc_bwd
-- lunit_bwd
-- =
-- bimap unhusk id <<< unzip <<< fmap lunit_bwd
-- runit_bwd
-- =
-- bimap id unhusk <<< unzip <<< fmap runit_bwd
Wenn wir über die Typen nachdenken, die an den Definitionen und Gesetzen beteiligt sind, wird die enttäuschende Wahrheit offenbart; OpApplicative
ist keine spezifischere Einschränkung als Functor
:
instance Functor f => OpApplicative f
where
unzip fab = (fst <$> fab, snd <$> fab)
unhusk = const ()
Obwohl jeder Applicative
Funktor (wirklich jeder Functor
) trivial ist OpApplicative
, gibt es nicht unbedingt eine schöne Beziehung zwischen den Applicative
Nachlässigkeiten und OpApplicative
Oplaxitäten. So können wir nach starken monoidalen Funktoren für die kartesische monoidale Struktur suchen :
class (Applicative f, OpApplicative f) => StrongApplicative f
-- Laws:
-- unhusk . husk = id
-- husk . unhusk = id
-- zip . unzip = id
-- unzip . zip = id
Das erste Gesetz oben ist trivial, da der einzige Bewohner des Typs () -> ()
die Identitätsfunktion ist ()
.
Die verbleibenden drei Gesetze und damit die Unterklasse selbst sind jedoch nicht trivial. Insbesondere ist nicht jeder Applicative
eine rechtmäßige Instanz dieser Klasse.
Hier sind einige Applicative
Funktoren, für die wir rechtmäßige Fälle deklarieren können StrongApplicative
:
Identity
VoidF
(->) r
(siehe Antworten)Monoid m => (,) m
Vec (n :: Nat)
Stream
(unendlich)
Und hier sind einige Applicative
s, für die wir nicht können:
[]
Either e
Maybe
NonEmptyList
Das Muster hier legt nahe, dass die StrongApplicative
Klasse in gewissem Sinne die FixedSize
Klasse ist, wobei "feste Größe" * bedeutet, dass die Vielzahl ** der Einwohner a
eines Bewohners vonf a
festgelegt ist.
Dies kann als zwei Vermutungen angegeben werden:
- Jeder,
Applicative
der einen Container "fester Größe" von Elementen seines Typarguments darstellt, ist eine Instanz vonStrongApplicative
- Es
StrongApplicative
gibt keine Instanz von , in der die Anzahl der Vorkommena
variieren kann
Kann sich jemand Gegenbeispiele vorstellen, die diese Vermutungen widerlegen, oder überzeugende Argumente, die zeigen, warum sie wahr oder falsch sind?
* Mir ist klar, dass ich das Adjektiv "feste Größe" nicht richtig definiert habe. Leider ist die Aufgabe etwas kreisförmig. Ich kenne keine formale Beschreibung eines Containers mit "fester Größe" und versuche, einen zu finden. StrongApplicative
ist mein bisher bester Versuch.
Um zu beurteilen, ob dies eine gute Definition ist, brauche ich etwas, mit dem ich sie vergleichen kann. Angesichts einer formellen / informellen Definition dessen, was es für einen Funktor bedeutet, eine bestimmte Größe oder Vielfalt in Bezug auf Einwohner seines Typarguments zu haben, stellt sich die Frage, ob die Existenz einer StrongApplicative
Instanz Funktoren fester und unterschiedlicher Größe genau unterscheidet.
Da mir eine bestehende formale Definition nicht bekannt ist, appelliere ich an die Intuition, wenn ich den Begriff "feste Größe" verwende. Allerdings, wenn jemand bereits einen bestehenden Formalismus für die Größe eines Funktors kennt und vergleichen kannStrongApplicative
besser.
** Mit "Multiplizität" beziehe ich mich in einem losen Sinne auf "wie viele" beliebige Elemente des Parametertyps des Funktors in einem Bewohner des Codomänen-Typs des Funktors vorkommen. Dies ist ohne Rücksicht auf den speziellen Typ der Funktor auf, angewendet wird und daher ohne Rücksicht auf irgendwelche spezifischen Bewohner des Parametertyps.
Nicht genau zu sein, hat einige Verwirrung in den Kommentaren verursacht. Hier sind einige Beispiele dafür, was ich für die Größe / Vielzahl verschiedener Funktoren halten würde:
VoidF
: fest, 0Identity
: fest, 1Maybe
: variabel, Minimum 0, Maximum 1[]
: variabel, Minimum 0, Maximum unendlichNonEmptyList
: variabel, Minimum 1, Maximum unendlichStream
: fest, unendlichMonoid m => (,) m
: fest, 1data Pair a = Pair a a
: fest, 2Either x
: variabel, Minimum 0, Maximum 1data Strange a = L a | R a
: fest, 1
quelle
(->) r
sie auf die richtige Weise isomorph sind.(->) r
; Sie benötigen die Komponenten des Isomorphismus, um die starke Anwendungsstruktur zu erhalten. Aus irgendeinem Grund hat dieRepresentable
Typklasse in Haskell ein mysteriösestabulate . return = return
Gesetz (das für nicht-monadische Funktoren nicht wirklich Sinn macht), aber es gibt uns 1/4 der Bedingungen, die wir dazu sagen müssen,tabulate
undzip
sind Morphismen einer geeigneten Kategorie von Monoiden . Die anderen 3 sind zusätzliche Gesetze, die Sie fordern müssen.tabulate
undindex
sind Morphismen einer geeigneten Kategorie ..." seinreturn
kein ernstes Problem ist.cotraverse getConst . Const
ist eine Standardimplementierung fürreturn
/pure
in Bezug aufDistributive
und da Distributives / Representables eine feste Form haben, ist diese Implementierung eindeutig.Antworten:
Ich bin mir bei dieser ersten Vermutung nicht sicher und aufgrund von Diskussionen mit @AsadSaeeduddin ist es wahrscheinlich schwierig zu beweisen, aber die zweite Vermutung ist wahr. Um zu sehen warum, betrachten Sie das
StrongApplicative
Gesetzhusk . unhusk == id
; das heißt für allex :: f ()
,husk (unhusk x) == x
. Aber in Haskell,unhusk == const ()
so ist das Gesetz für alle gleichwertig zu sagenx :: f ()
,husk () == x
. Dies impliziert jedoch wiederum, dass es nur einen bestimmten Wert vom Typ geben kannf ()
: Wenn es zwei Werte gäbex, y :: f ()
, dannx == husk ()
undhusk () == y
sox == y
. Wenn es jedoch nur einen möglichenf ()
Wert gibt,f
muss er eine feste Form haben. (zB fürdata Pair a = Pair a a
gibt es nur einen Wert vom TypPair ()
, dies istPair () ()
, aber es gibt mehrere Werte vom TypMaybe ()
oder[()]
.) Alsohusk . unhusk == id
impliziert, dassf
die Form fest sein muss.quelle
f ()
" impliziert, dass "die Anzahl der Vorkommen vona
nicht variieren kann", wenn ausgefallene GADTs und ähnliches vorhanden sind?a
kann nicht variieren" keine ausreichende Bedingung für eineStrongApplicative
Instanz ist. hat zum Beispieldata Writer w a = Writer (w,a)
eine nicht variierende Vielzahl vona
, ist aber keineStrongApplicative
. Sie brauchen tatsächlich die Form des Funktors, um unveränderlich zu sein, was meiner Meinung nach eine Folge davonf ()
ist, ein Singleton zu sein.f ()
" impliziert "Anzahl der Vorkommen vona
kann nicht variieren". Ich beanstande, dass der letzte Schritt dieses Arguments nicht eindeutig wahr ist; zB überlegendata Weird a where One :: a -> Weird a; None :: Weird Bool
. Es gibt einen bestimmten Wert für den TypWeird ()
, aber verschiedene Konstruktoren enthalten eine unterschiedliche Anzahl vona
s. (Es ist hier kein vollständiges Gegenbeispiel, weilFunctor
es schwierig ist, aber woher wissen wir, dass das nicht behoben werden kann?)Weird ()
ein Singleton ist, aber keine feste Form hat. Aber esWeird
ist keinFunctor
, also kann esStrongApplicative
sowieso kein sein . Ich nehme an, die relevante Vermutung wäre: Wennf
aFunctor
, bedeutetf ()
ein Singleton, dassf
es eine feste Form hat ? Ich vermute sehr, dass dies wahr ist, aber wie Sie bemerkt haben, habe ich noch keinen Beweis.Wir können mindestens eine dieser Fragen verneinen:
In der Tat ist eines der Beispiele für ein Gesetz
StrongApplicative
in der ursprünglichen Frage falsch. Der SchriftstellerMonoid => (,) m
ist nicht anwendbarStrongApplicative
, weil zum Beispielhusk $ unhusk $ ("foo", ()) == ("", ()) /= ("foo", ())
.Ebenso das Beispiel eines Containers mit fester Größe:
der festen Multiplizität 1 ist keine starke Anwendung, denn wenn wir
husk = Left
dann definierenhusk $ unhusk $ Right () /= Right ()
und umgekehrt. Eine äquivalente Möglichkeit, dies anzuzeigen, besteht darin, dass dies nur der Autor ist, der für Ihre Wahl des Monoids auf anwendbar istBool
.Es gibt also Anwendungen mit "fester Größe", die dies nicht sind
StrongApplicative
. Ob alleStrongApplicative
s eine feste Größe haben, bleibt abzuwarten.quelle
Nehmen wir darstellbare Funktoren als unsere Definition von "Container mit fester Größe":
Der Real
Representable
hat ein paar Gesetze und Oberklassen, aber für die Zwecke dieser Antwort benötigen wir tatsächlich nur zwei Eigenschaften:(Okay, wir brauchen auch eine gesetzestreue
instance StrongApplicative ((->) r)
. Einfach peasy, Sie stimmen bereits zu, dass es existiert.)Wenn wir diese Definition nehmen, kann ich diese Vermutung 1 bestätigen:
ist wahr. Hier ist wie:
Es gibt eine Menge Gesetze zu beweisen, aber ich werde mich nur auf die Big Four konzentrieren, die
StrongApplicative
hinzufügen - Sie glauben wahrscheinlich bereits an die führenden Gesetze fürApplicative
undOpApplicative
, wenn Sie dies nicht tun, sehen ihre Beweise genauso aus wie die folgenden ( die wiederum ziemlich ähnlich aussehen). Aus Gründen der Klarheit werde ich verwendenzipf
,huskf
etc. für die Funktionsinstanz, undzipr
,huskr
etc. für die darstellbare Instanz, so dass Sie verfolgen, welche zu halten ist , welche. (Und damit es einfach ist zu überprüfen, ob wir das, was wir zu beweisen versuchen, nicht als Annahme nehmen! Es ist in Ordnung, esunhuskf . huskf = id
beim Beweisen zu verwendenunhuskr . huskr = id
, aber es wäre falschunhuskr . huskr = id
, denselben Beweis anzunehmen .)Der Beweis für jedes Gesetz erfolgt im Wesentlichen auf die gleiche Weise: Entrollen Sie Definitionen, lassen Sie den Isomorphismus fallen,
Representable
den Sie erhalten, und verwenden Sie dann das analoge Gesetz für Funktionen.quelle
instance StrongApplicative f => Representable f where type Rep f = forall x. f x -> x
.index
ist einfach. Ich habe den Tricktabulate
noch nicht ausgearbeitet , aber er scheint verlockend nah zu sein.StrongApplicative
Instanz ebenfalls finden, konnte aber die Gesetze nicht beweisen. Herzlichen Glückwunsch zum Herausfinden! Ich habe versucht, dieRepresentable
Instanz auch gut zu machenStrongApplicative
, konnte mir aber keinen gutenRep
Typ vorstellen - ich wäre gespannt, wie Sie diesforall x. f x -> x
erreichen?forall x. f x -> x
genau die Funktionen, die ein Loch auswählen und den Wert in diesem Loch zurückgeben. (Und währendtabulate
ich über die Implementierung nachdachte , habe ich einen Einwand gegen den Typ fürunhusk
; siehe Kommentare zur Frage selbst für Details.)forall x. f x -> x
es so funktionieren wirdRep
. Meine Argumentation ist, dassRep
Sie damitindex
für jeden Typ schreiben können , nicht nur für einenStrongApplicative
- ich vermute also, dassforall x. f x -> x
dies zu allgemein ist.