Типы Haskell - это не просто наборы изолированных значений;они могут иметь операции, которые объединяют два элемента одного типа и возвращают еще один элемент типа.Например, String
с ++
, Natural
с +
и *
, Bool
с &&
и ||
.
Эти операции могут удовлетворять или не удовлетворять некоторым свойствам.Например, ассоциативное свойство (удовлетворяемое всеми операциями, упомянутыми ранее) или коммутативное свойство (этот список добавляется ++
не удовлетворяет).Иногда свойство связывает две разные операции, например, закон распределения, относящийся к +
и *
.
Предположим, у нас есть два разных типа, а также две разные операции, по одной для каждого типа.Например, String
и ++
с одной стороны и Natural
и +
с другой.Операции разные, но если немного косить, мы видим, что они оба удовлетворяют ассоциативному свойству.
Теперь, что если бы мы могли найти функцию, которая преобразует String
значения в Natural
значения, втаким образом, чтобы +
результаты преобразования двух String
s всегда были равны результату преобразования ++
исходного String
s?Это сложнее, чем просто найти какую-либо функцию типа String -> Natural
.Это должна быть функция, которая сохраняет результаты операций при переходе на другую сторону.Эта функция между двумя типами называется гомоморфизмом .
Например, функция length :: String -> Natural
является гомоморфизмом.Длина объединения двух строк равна сумме исходных длин.Функция, подобная length
, но которая присваивает ненулевое значение пустому списку, не будет допустимым гомоморфизмом.
Обратите внимание, что гомоморфизм может «стереть» различия, присутствующие в типе источника.Например, length
присваивает один и тот же номер "foo"
и "bar"
.
Другой пример: рассмотрим тип FilePath
и операцию </>
с одной стороны (давайтеучитывайте только относительные пути к папкам), а также введите IO ()
и операцию >>
с другой.Тогда функция setCurrentDirectory :: FilePath -> IO ()
является гомоморфизмом.Обратите внимание, что существуют значения IO ()
, например putStrLn "foo"
, которые не представляют эффект «изменить папку» и никогда не «нацеливаются» на setCurrentDirectory
.Этого не произошло с lenght
, где каждый Natural
был lenght
какого-то String
или другого.