Подтвердить что ты не робот

Почему Складные и Functor отдельные классы?

В разделе wiki говорится, что оба класса имеют дело с контейнерными операциями, а Foldable - это класс контейнеров с foldr, определенный над ними, и для Functor it fmap.

Однако каково принципиальное различие между типами Foldable и типами, которые являются функторами?

Вики намекают на это различие:

Класс [Foldable] не требует суперкласса Functor, чтобы разрешить контейнеры, такие как Set или StorableVector

Но я все еще не уверен, почему Set не удалось сопоставить, чтобы получить другой набор, если я правильно его интерпретирую.

4b9b3361

Ответ 1

Foldable и Functor предлагают две отдельные абстракции для типов со структурами, которые могут складываться (или уменьшаться) и отображаться соответственно.

A Складные значения, которые могут быть перечислены и объединены вместе 1. Можно думать о Складном как о чем-то, что можно превратить в список (toList :: Foldable f => f a -> [a]). В качестве альтернативы можно думать о Foldables как о структурах, значения которых могут быть объединены моноидально: (Foldable t, Monoid m) => (a -> m) -> t a -> m (конечно, для этого требуется возможность их перечислить).

Функторы, с другой стороны, являются структурами, которые позволяют "поднять" функцию (a -> b) для применения к a, удерживаемому структурой (fmap :: (a -> b) -> (f a -> f b)). fmap должен сохранять отображаемую структуру: дерево должно иметь одинаковую форму до и после, список должен иметь одинаковое количество элементов в том же порядке, Nothing нельзя превратить в что-то и т.д., С другой стороны, Foldables не должны сохранять эту структуру; все дело в том, чтобы отбросить структуру и создать новую.

Wiki ссылается на тот факт, что для ограничения типа typeclass нет fmap. fmap :: (Ord a, Ord b) => (a -> b) -> Set a -> Set b не объединяется с типом, определенным классом, fmap :: (a -> b) -> f a -> f b, который не имеет ограничений. Это делает невозможным запись экземпляра для Set.

Однако это просто проблема языковой реализации, а не более глубокое математическое утверждение о наборах. Настоящая причина, по которой Foldable не имеет суперкласса Functor, просто состоит в том, что есть Foldable экземпляры, которые не являются экземплярами Functor.


  • "Hold" немного ослаблен и предназначен для интерпретации в "Функторы являются контейнерами" , где Proxy s a имеет нулевое значение a, Identity a содержит один a, Maybe a имеет нуль или один a, b -> a содержит |b| a и т.д.

Ответ 2

Предположим, что у меня есть xs :: Set Int, и я хочу отобразить над ним функцию putStrLn. Это будет проблемой, потому что IO () не имеет экземпляра Ord, поэтому нет способа выяснить, как вставить эти действия в набор результатов. Тип fmap не оставляет места для ограничений для аргумента типа Functor.

IO также дает пример того, что a Functor, но нет никакого значимого способа foldMap.

Стоит отметить, что Foldable и Functor объединяются в класс Traversable, который дает значительно большую мощность, чем либо.

Ответ 3

Set и StorableVector являются функторами, вы действительно можете отображать над ними функции.

Но не какая-либо функция. Например, вы не можете сопоставить (+) над StorableArray чисел: это даст массив функций, и они не будут сохраняться.

Итак, эти функторы не являются (endo-) функторами на всех Hask, но только в подкатегории, включающей типы определенного класса. Это не может быть выражено в Haskell98, на самом деле это стало возможным совсем недавно с появлением видов ограничений. См. этот пример:

instance Functor Set Ranking Ranking where
  fmap = constrainedFmap Set.map

На самом деле, наборы также образуют монаду в Hask, если вы используете некоторые умные трюки GADT. Но это невозможно для всех контейнеров Foldable, поэтому стандартной библиотеке не требуется Functor f => Foldable f.

Ответ 4

Functor typeclass не позволяет вам fmap над вещами, которые имеют ограничения на типы их элементов. Set требует Ord и StorableVector требует Storable.

Можно выразить "ограниченный функтор" с использованием расширения GHC ConstraintKinds, что-то вроде:

{-# LANGUAGE ConstraintKinds,
             FunctionalDependencies,
             MultiParamTypeClasses #-}

import GHC.Exts (Constraint)
import Data.Set (Set)
import qualified Data.Set as Set

class ConstrainedFunctor c f | f -> c where
  cfmap :: (c a, c b) => (a -> b) -> f a -> f b

instance ConstrainedFunctor Ord Set where
  cfmap = Set.map

Но этот механизм не был вокруг, когда появился Functor.

Кроме того, Functor должен быть "сохраняющим форму", но, например, отображение через Set может изменить его размер.