Прочитав книгу Учите вас Haskell For Great Good и очень полезную статью в wiki-книге Теория категорий Haskell, которая помогла мне преодолеть общую ошибку категории путающих объектов категории с объектами программирования, я все еще имею следующий вопрос:
Зачем нужно fmap отображать все элементы списка?
Мне нравится, что это так, я просто хочу понять, как это теоретически обоснована категория. (или, возможно, проще обосновать использование HoTT?)
В обозначениях Scala List - функтор, который принимает любой тип и отображает его в тип из множества всех типов списков, например, он сопоставляет тип Int с типом List[Int] и отображает его функции на Int например,
-
Int.successor: Int => InttoFunctor[List].fmap(successor) : List[Int] => List[Int] -
Int.toString: Int => StringtoFunctor[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)