Прочитав книгу Учите вас Haskell For Great Good и очень полезную статью в wiki-книге Теория категорий Haskell, которая помогла мне преодолеть общую ошибку категории путающих объектов категории с объектами программирования, я все еще имею следующий вопрос:
Зачем нужно fmap
отображать все элементы списка?
Мне нравится, что это так, я просто хочу понять, как это теоретически обоснована категория. (или, возможно, проще обосновать использование HoTT?)
В обозначениях Scala List
- функтор, который принимает любой тип и отображает его в тип из множества всех типов списков, например, он сопоставляет тип Int
с типом List[Int]
и отображает его функции на Int
например,
-
Int.successor: Int => Int
toFunctor[List].fmap(successor) : List[Int] => List[Int]
-
Int.toString: Int => String
toFunctor[List].fmap(toString): List[Int] => List[String]
Теперь каждый экземпляр List[X]
является моноидом с empty
function (mempty
в Haskell) и combine
function (mappend
в Haskell). Я предполагаю, что можно использовать тот факт, что списки являются моноидами, чтобы показать, что map
должен отображать все элементы списка. Мое чувство здесь заключается в том, что если добавить pure
функцию из аппликативного, это дает нам список только с одним элементом другого типа. например, Applicative[List[Int]].pure(1) == List(1)
. Так как map(succ)
на этих элементах дает нам одноэлементный список со следующим элементом, это охватывает все эти подмножества. Тогда я полагаю, что функция combine
на всех этих синглетонах дает нам все остальные элементы списков. Почему-то я полагаю, что это ограничивает способ работы карты.
Другим наводящим аргументом является то, что map
должен отображать функции между списками. Так как каждый элемент из a List[Int]
имеет тип 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)