Ich versuche diese Frage aus meinen Hausaufgaben zu machen:
Wenn Sie willkürlich sind
foo :: [[a]] -> ([a], [a])
, schreiben Sie ein Gesetz auf, das die Funktionfoo
erfüllt, und zwarmap
in Listen und Paaren.
Ein Zusammenhang: Ich bin ein Student im ersten Jahr, der einen Kurs in funktionaler Programmierung belegt. Während der Kurs eher einführend ist, hat der Dozent viele Dinge aus dem Lehrplan heraus erwähnt, darunter die freien Theoreme. Nachdem ich versucht hatte, Wadlers Artikel zu lesen, rechnete ich damit, dass concat :: [[a]] -> [a]
das Gesetz map f . concat = concat . map (map f)
für mein Problem relevant erscheint, da wir foo xss = (concat xss, concat' xss)
wo concat
und concat'
welche Funktionen vom Typ haben müssen [[a]] -> [a]
. Dann foo
befriedigt bimap (map f, map g) . foo = \xss -> ((fst . foo . map (map f)) xss, (snd . foo . map (map g)) xss)
.
Schon dieses 'Gesetz' scheint zu lang, um richtig zu sein, und ich bin mir auch meiner Logik nicht sicher. Also habe ich darüber nachgedacht, einen kostenlosen Online-Theoremgenerator zu verwenden , aber ich verstehe nicht, was lift{(,)}
bedeutet:
forall t1,t2 in TYPES, g :: t1 -> t2.
forall x :: [[t1]].
(f x, f (map (map g) x)) in lift{(,)}(map g,map g)
lift{(,)}(map g,map g)
= {((x1, x2), (y1, y2)) | (map g x1 = y1) && (map g x2 = y2)}
Wie soll ich diese Ausgabe verstehen? Und wie soll ich das Gesetz für die Funktion foo
richtig ableiten ?
quelle
(\(a,b) -> (map f a, map f b)) . foo = foo . map (map f)
Antworten:
Wenn
R1
undR2
Beziehungen sind (sagen wirR_i
zwischenA_i
undB_i
miti in {1,2}
), dannlift{(,)}(R1,R2)
sind die "aufgehobenen" Beziehungspaare zwischenA1 * A2
undB1 * B2
mit der*
Bezeichnung des Produkts (geschrieben(,)
in Haskell).In der gelebten Beziehung sind zwei Paare
(x1,x2) :: A1*A2
und(y1,y2) :: B1*B2
genau dann verwandt, wennx1 R1 y1
undx2 R2 y2
. In Ihrem FallR1
undR2
sind Funktionenmap g, map g
, so dass das Heben auch eine Funktion wird :y1 = map g x1 && y2 = map g x2
.Daher das erzeugte
meint:
oder mit anderen Worten:
was ich schreiben würde, mit
Control.Arrow
:oder sogar im punktfreien Stil:
Dies ist keine Überraschung, da Sie
f
als geschrieben werden könnenund
F
,G
sind functors (in Haskell bräuchten wir ein verwenden ,newtype
eine Funktor Instanz zu definieren, aber ich werde das nicht angeben, da es irrelevant). In einem solchen häufigen Fall hat der freie Satz eine sehr schöne Form: für jedeng
,Dies ist eine sehr schöne Form, genannt
f
Natürlichkeit ( kann als natürliche Transformation in einer geeigneten Kategorie interpretiert werden). Beachten Sie, dass die beidenf
oben genannten s tatsächlich auf separaten Typen instanziiert werden, damit die Typen mit dem Rest übereinstimmen.In Ihrem speziellen Fall ist es, da
F a = [[a]]
es die Zusammensetzung des[]
Funktors mit sich selbst ist, daher erhalten wir (nicht überraschend)fmap_of_F g = fmap_of_[] (fmap_of_[] g) = map (map g)
.Stattdessen
G a = ([a],[a])
ist die Zusammensetzung der Funktoren[]
undH a = (a,a)
(technisch gesehen diagonale Funktoren mit dem Produktfunktor zusammengesetzt). Wir habenfmap_of_H h = (h *** h) = (\x -> (h x, h x))
, von denenfmap_of_G g = fmap_of_H (fmap_of_[] g) = (map g *** map g)
.quelle
g
insgesamt verlangen . Ebenso müssenseq
wir , da wir habeng
, streng sein müssen. Ich bin mir über die genauen Einschränkungen nicht 100% sicher, aber ich denke, das sollte es sein. Ich kann mich jedoch nicht erinnern, wo ich darüber gelesen habe - wahrscheinlich gibt es auf der Seite mit dem kostenlosen Theoremgenerator einige Informationen.map
vsfmap
. Die Leute benutzen weiter,map
da es offensichtlich ist, dass es sich um Listen handelt (und nicht um andere Funktionen). Ebenso(***)
funktioniert nur bei Paaren (und nicht bei anderen Bifunktoren). Ich benutze es wahrscheinlich hauptsächlich wegen seiner Infix-Ness, da wir in der Mathematik dazu neigen, zu schreibenf \times g
, um den Produkt-Bifunctor anzuwenden. Vielleichtbimap
sollte es auch eine Infix-Variante geben, wie<$>
es eine Variante für istfmap
.(***)
es spezifischer ist, alsbimap
dass es nur für Paare und nicht für beliebige Bifunktoren funktioniert, aber es ist auch wahr, dassbimap
es spezifischer ist, als(***)
dass es nur für Funktionen und nicht für beliebige Pfeile funktioniert. Re infix, das wäre nicht ganz das gleiche fürbimap
undfmap
, dabimap
3 Parameter undfmap
nur 2 benötigt.Das Gleiche wie die Antwort von @ chi mit weniger Zeremonie:
Es spielt keine Rolle, ob Sie das
a
sb
vor oder nach der Funktion in s ändern , Sie erhalten dasselbe (solange Sie einfmap
ähnliches Element verwenden).quelle