Nachdem das Buch Learn you a Haskell For Great Good und das sehr hilfreich Wiki Buch Artikel Haskell Category Theory lesen, die mich of confusing category objects with the programming objects, die gemeinsame Kategorie Fehler zu überwinden half ich habe immer noch die folgende Frage:Warum muss fmap jedes Element einer Liste zuordnen?
Warum muss fmap
Karte über alle Elemente einer Liste?
Ich mag, dass es tut, ich möchte nur verstehen, wie dies theoretisch Kategorie gerechtfertigt ist. (Oder vielleicht ist es einfacher, mit HoTT zu rechtfertigen?)
In Scala Notation List
ein Funktor ist, dass jede Art nimmt und Karten, die in eine Art aus der Menge aller Listentypen, zB es bildet den Typ Int
auf die Typ List[Int]
und ordnet die Funktionen auf Int
zB
Int.successor: Int => Int
zuFunctor[List].fmap(successor) : List[Int] => List[Int]
Int.toString: Int => String
zuFunctor[List].fmap(toString): List[Int] => List[String]
Nun ist jede Instanz von List[X]
ein Monoid mit einem empty
function (mempty
in Haskell) und combine
function (mappend
in Haskell). Meine Vermutung ist, dass man die Tatsache, dass Listen Monoids sind, verwenden kann, um zu zeigen, dass map
alle Elemente einer Liste abbilden muss. Mein Gefühl hier ist, dass, wenn man die pure
function from Applicative hinzufügt, dies uns eine Liste mit nur einem Element eines anderen Typs gibt. z.B. Applicative[List[Int]].pure(1) == List(1)
. Da map(succ)
für diese Elemente die Singleton-Liste mit dem nächsten Element enthält, deckt dies alle diese Teilmengen ab. Dann nehme ich an, dass die Funktion combine
auf all diesen Singletons uns alle anderen Elemente der Listen liefert. Irgendwie schränkt das die Art und Weise ein, wie die Karte funktionieren muss.
Ein weiteres suggestives Argument ist, dass map
Funktionen zwischen Listen zuordnen muss. Da jedes Element in einer List[Int]
vom Typ Int ist, und wenn man auf List[String]
abbildet, muss man jedes Element davon abbilden, oder man würde nicht den richtigen Typ verwenden.
Also scheinen beide Argumente in die richtige Richtung zu zeigen. Aber ich fragte mich, was nötig war, um den Rest des Weges zu bekommen.
Gegenbeispiel?
Warum ist das keine Gegenbeispiel-Map-Funktion?
def map[X,Y](f: X=>Y)(l: List[X]): List[Y] = l match {
case Nil => Nil
case head::tail=> List(f(head))
}
Es scheint, die Regeln
val l1 = List(3,2,1)
val l2 = List(2,10,100)
val plus2 = (x: Int) => x+ 2
val plus5 = (x: Int) => x+5
map(plus2)(List()) == List()
map(plus2)(l1) == List(5)
map(plus5)(l1) == List(8)
map(plus2 compose plus5)(l1) == List(10)
(map(plus2)_ compose map(plus5)_)(l1) == List(10)
Ahh zu folgen. Aber es passt nicht zum id-Gesetz. Diese
def id[X](x: X): X = x
map(id[Int] _)(l1) == List(3)
id(l1) == List(3,2,1)
könnte 'foo :: a -> a' nicht die Nachfolgerfunktion sein? "succ :: Int => Int" ist nicht die Identitätsfunktion.succ 1 == 2 –
Ich habe Schwierigkeiten, dies für den Moment zu verfolgen, weil ich auf der Frage im vorherigen Kommentar stecken geblieben bin. In jedem Fall sollten Sie überprüfen, wie Theorems for Free mit HoTT zusammenpasst. [Anscheinend ist es nicht so einfach] (https://groups.google.com/d/msg/hott-cafe/ak301lhl9Qs/TLUDoah40pkJ) und kann dort falsch sein ... –
@HenryStory: 'succ' hat den Typ' Int -> Int', was nicht dasselbe ist wie 'a -> a', also' forall a. a -> a'. Der Typ von "succ" ist spezifischer als "a -> a", und "map succ" ist gut typisiert, aber nach map type muss "map" für alle derartigen Funktionen funktionieren und kann daher nicht annehmen irgendetwas über sie. – opqdonut