[英]Why must fmap map every element of a List?
閱讀了這本書, 了解了一本Haskell For Great Good ,以及非常有用的維基書籍Haskell分類理論幫助我克服了將類別對象與編程對象混淆的常見類別錯誤,我仍然有以下問題:
為什么fmap
必須映射List的每個元素?
我喜歡它,我只想理解這在理論上是如何合理的。 (或者更容易證明使用HoTT?)
在Scala表示法中, List
是一個仿函數,它接受任何類型並將其映射到所有列表類型集合中的類型,例如,它將類型Int
映射到類型List[Int]
並將它映射到Int
上的函數,例如
Int.successor: Int => Int
to Functor[List].fmap(successor) : List[Int] => List[Int]
Int.toString: Int => String
to Functor[List].fmap(toString): List[Int] => List[String]
現在, List[X]
每個實例都是一個具有empty
函數 (在Haskell中為mempty
)和combine
函數 (在Haskell中為mappend
)的mappend
。 我的猜測是,人們可以使用Lists是Monoids這一事實來表明map
必須映射列表的所有元素。 我的感覺是,如果從Applicative添加pure
函數 ,這給了我們一個只包含其他類型的元素的列表。 例如Applicative[List[Int]].pure(1) == List(1)
。 由於這些元素上的map(succ)
為我們提供了包含下一個元素的單例列表,因此它涵蓋了所有這些子集。 然后我想所有這些單例的combine
函數為我們提供了列表中所有其他元素。 不知怎的,我想這限制了地圖的工作方式。
另一個有爭議的論點是map
必須在列表之間映射函數。 由於List[Int]
中的每個元素都是List[Int]
類型,並且如果一個元素映射到List[String]
則必須映射它的每個元素,或者一個不是正確的類型。
因此,這兩個論點似乎都指向了正確的方向。 但我想知道剩下的方式需要什么。
反例?
為什么這不是反例地圖功能?
def map[X,Y](f: X=>Y)(l: List[X]): List[Y] = l match {
case Nil => Nil
case head::tail=> List(f(head))
}
它似乎遵循規則
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)
啊。 但它不符合id法。
def id[X](x: X): X = x
map(id[Int] _)(l1) == List(3)
id(l1) == List(3,2,1)
這依賴於稱為“參數化”的理論結果,首先由雷諾茲定義,然后由瓦德勒(以及其他人)開發。 也許關於這個主題的最着名的論文是“免費的定理!” 由瓦德勒。
其核心思想是,從多態型只有一個功能,我們可以得到關於函數的語義的一些信息。 例如:
foo :: a -> a
僅從這種類型,我們可以看到,如果foo
終止,它就是身份功能。 直觀地說, foo
無法區分不同a
s,因為在Haskell中我們沒有例如可以檢查實際運行時類型的Java的instanceof
。 同樣的,
bar :: a -> b -> a
必須返回第一個參數。 並且baz :: a -> a -> a
必須返回第一個或第二個。 並且quz :: a -> (a -> a) -> a
必須將函數的某些固定次數應用於第一個參數。 你現在可能已經明白了。
可以從類型推斷的一般屬性非常復雜,但幸運的是它可以通過機械計算。 在范疇論中,這與自然變換的概念有關。
對於map
類型,我們獲得以下可怕屬性:
forall t1,t2 in TYPES, f :: t1 -> t2.
forall t3,t4 in TYPES, g :: t3 -> t4.
forall p :: t1 -> t3.
forall q :: t2 -> t4.
(forall x :: t1. g (p x) = q (f x))
==> (forall y :: [t1].
map_{t3}_{t4} g (map2_{t1}_{t3} p y) =
map2_{t2}_{t4} q (map_{t1}_{t2} f y))
在上面, map
是眾所周知的map函數,而map2
是任意函數,它具有類型(a -> b) -> [a] -> [b]
。
現在,進一步假設map2
滿足map2
函數定律,特別是map2 id = id
。 然后我們可以選擇p = id
和t3 = t1
。 我們得到了
forall t1,t2 in TYPES, f :: t1 -> t2.
forall t4 in TYPES, g :: t1 -> t4.
forall q :: t2 -> t4.
(forall x :: t1. g x = q (f x))
==> (forall y :: [t1].
map_{t1}_{t4} g (map2_{t1}_{t1} id y) =
map2_{t2}_{t4} q (map_{t1}_{t2} f y))
在map2
上應用map2
函數法則:
forall t1,t2 in TYPES, f :: t1 -> t2.
forall t4 in TYPES, g :: t1 -> t4.
forall q :: t2 -> t4.
(forall x :: t1. g x = q (f x))
==> (forall y :: [t1].
map_{t1}_{t4} g y =
map2_{t2}_{t4} q (map_{t1}_{t2} f y))
現在,讓我們選擇t2 = t1
和f = id
:
forall t1 in TYPES.
forall t4 in TYPES, g :: t1 -> t4.
forall q :: t1 -> t4.
(forall x :: t1. g x = q x)
==> (forall y :: [t1].
map_{t1}_{t4} g y =
map2_{t1}_{t4} q (map_{t1}_{t1} id y))
按map
的仿函數定律:
forall t1, t4 in TYPES.
forall g :: t1 -> t4, q :: t1 -> t4.
g = q
==> (forall y :: [t1].
map_{t1}_{t4} g y =
map2_{t1}_{t4} q y)
意思是
forall t1, t4 in TYPES.
forall g :: t1 -> t4.
(forall y :: [t1].
map_{t1}_{t4} g y =
map2_{t1}_{t4} g y)
意思是
forall t1, t4 in TYPES.
map_{t1}_{t4} = map2_{t1}_{t4}
加起來:
如果map2
是具有多態類型(a -> b) -> [a] -> [b]
任何函數,並且它滿足第一個map2 id = id
函數法map2 id = id
,那么map2
必須等同於標准map
函數。
另請參閱Edward Kmett撰寫的相關博客文章 。
請注意,在Scala中,只有在不使用x.isInstanceOf[]
和其他可能會破壞參數的反射工具時,上述情況才會成立。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.