Множества, функторы и путаница эквалайзера



недавно на работе возникла дискуссия о наборах, которые в Scala поддерживают zip метод и как это может привести к ошибкам, например,



scala> val words = Set("one", "two", "three")
scala> words zip (words map (_.length))
res1: Set[(java.lang.String, Int)] = Set((one,3), (two,5))


я думаю, что это довольно ясно, что Sets не должен поддерживать a zip операция, так как элементы не упорядочены. Однако было высказано предположение, что проблема заключается в том, что Set на самом деле не функтор, и не должен иметь map метод. Конечно,вы можете попасть в беду, сопоставляя набор. Переключение на Хаскелл сейчас,



data AlwaysEqual a = Wrap { unWrap :: a }

instance Eq (AlwaysEqual a) where
_ == _ = True

instance Ord (AlwaysEqual a) where
compare _ _ = EQ


а теперь в ghci



ghci> import Data.Set as Set
ghci> let nums = Set.fromList [1, 2, 3]
ghci> Set.map unWrap $ Set.map Wrap $ nums
fromList [3]
ghci> Set.map (unWrap . Wrap) nums
fromList [1, 2, 3]


так Set не удовлетворяет закону функтора



    fmap f . fmap g = fmap (f . g)


можно утверждать, что это не ошибка map работы на Set s, но провал Eq экземпляр, который мы определили, потому что он не уважает закон подстановки, а именно, что для двух экземпляров Eq на A и B и отображение f : A -> B затем



    if x == y (on A) then f x == f y (on B)


который не держится за AlwaysEqual (например, рассмотрим f = unWrap).



является ли закон замещения разумным законом для Eq типа, что мы должны стараться уважать? Конечно, другие законы равенства уважаются нашими AlwaysEqual тип (симметрия, транзитивность и рефлексивность тривиально удовлетворены), поэтому замена-единственное место, где мы можем попасть в беду.



для меня подстановка кажется очень желательным свойством для Eq класса. С другой стороны, некоторые комментарии на недавний Reddit обсуждение включить




"подстановка кажется сильнее, чем необходимо, и в основном определяет тип, предъявляя требования к каждой функции, использующей тип."



--godofpumpkins



" я также действительно не хочу подстановки/конгруэнтности, поскольку есть много законных применений для ценностей, которые мы хотим приравнять, но как-то различимы."



--sclv



"замещение имеет место только для структурного равенства, но ничто не настаивает Eq является структурным."



-- edwardkmett




эти трое довольно хорошо известны в сообществе Haskell, поэтому я бы не решился пойти против них и настаивать на замене для моего Eq типы!



еще один аргумент против Set будучи Functor - широко признано, что быть Functor позволяет преобразовать "элементы" из "коллекции" при сохранении формы. Например, эта цитата на Haskell wiki (обратите внимание, что Traversable обобщение Functor)




"где Foldable дает вам возможность пройти через структуру обработки элементов, но выбрасывая форму,Traversable позволяет сделать это, сохраняя форму и, например, ввод новых значений."



"Traversable о сохранении структуры точно как есть."




а в реальном мире Хаскелл




"...[A] функтор должен сохранять форму. На структуру коллекции не должен влиять функтор; должны изменяться только значения, которые он содержит."




очевидно, что любой экземпляр функтора для Set имеет возможность изменять форму, уменьшая количество элементов в наборе.



но кажется, что Sets действительно должны быть функторы (игнорируя Ord требование на данный момент-я вижу это как искусственное ограничение, наложенное нашим желанием эффективно работать с множествами, а не абсолютное требование для любого множества. Например, наборы функций-это совершенно разумная вещь для рассмотрения. Во всяком случае, Олег показывает как написать эффективные экземпляры функтора и монады для Set не требуется Ord ограничения). Есть просто слишком много хороших применений для них (то же самое верно для несуществующего Monad экземпляр.)



может кто-нибудь прояснить этот бардак? Должен Set быть Functor? Если да, то что делать с потенциалом нарушения законов функтора? Для чего нужны законы Eq быть, и как они взаимодействуют с законами для Functor и Set экземпляра в частности?

705   3  

3 ответов:

еще один аргумент против Set будучи Functor - общепризнано, что быть Functor позволяет преобразовывать "элементы ""коллекции" при сохранении формы. [...] Очевидно, что любой экземпляр функтора для набора имеет возможность изменять форму, уменьшая количество элементов в наборе.

я боюсь, что это случай принятия аналогии "формы" в качестве определяющего условия, когда это не так. Математически говоря, там есть такая вещь, как функтор набора мощности. Из Википедии:

власть устанавливает: функтор набора мощности P: Set → Set отображает каждый набор в свой набор мощности и каждую функцию f: X → Y на карту, которая отправляет U ⊆ X на свое изображение f (U) ⊆ Y.

функция P (f) (fmap f в функторе набора мощности) не сохраняет размер своего набора аргументов, но тем не менее это функтор.

если вы если нужна непродуманная интуитивная аналогия, мы могли бы сказать следующее: в структуре, подобной списку, каждый элемент "заботится" о своем отношении к другим элементам и был бы "оскорблен", если бы ложный функтор нарушил эту связь. Но набор-это предельный случай: структура, элементы которой безразличны друг к другу, поэтому вы очень мало можете сделать, чтобы" оскорбить " их; единственное, если ложный функтор должен был сопоставить набор, содержащий этот элемент, с результатом, который не включает его "голос."

(хорошо, я сейчас заткнусь...)


EDIT: я усек следующие биты, когда я процитировал вас в верхней части моего ответа:

например, эта цитата на Haskell wiki (обратите внимание, что Traversable обобщение Functor)

"где Foldable дает вам возможность пройти через структуру обработки элементов, но выбрасывая форму,Traversable позволяет это сделать сохраняя при этом форму и, например, вводя новые значения."

"Traversable о сохранении структуры точно как есть."

вот я бы заметил, что Traversable вид специализированныйFunctor, а не "обобщение" его. Один из ключевых фактов о любом Traversable (или, собственно, о Foldable, который Traversable extends) заключается в том, что он требует, чтобы элементы любой структуры имели линейный порядок-вы можете превратить любой Traversable в список его элементов (с Foldable.toList).

еще один, менее очевидный факт о Traversable это то, что существуют следующие функции (адаптированные из Gibbons & Oliveira, "сущность шаблона итератора"):

-- | A "shape" is a Traversable structure with "no content," 
-- i.e., () at all locations.
type Shape t = t ()

-- | "Contents" without a shape are lists of elements.
type Contents a = [a]

shape :: Traversable t => t a -> Shape t
shape = fmap (const ())

contents :: Traversable t => t a -> Contents a
contents = Foldable.toList

-- | This function reconstructs any Traversable from its Shape and
-- Contents.  Law:
--
-- > reassemble (shape xs) (contents xs) == Just xs
--
-- See Gibbons & Oliveira for implementation.  Or do it as an exercise.
-- Hint: use the State monad...
--
reassemble :: Traversable t => Shape t -> Contents a -> Maybe (t a)

A Traversable экземпляр для множеств будет нарушать предлагаемый закон, потому что все непустые множества будут иметь одно и то же Shape-набор которого Contents и [()]. Из этого должно быть легко доказать, что всякий раз, когда вы пытаетесь reassemble набор Вы бы только когда-нибудь получить пустой набор или синглтон обратно.

урок? Traversable "сохраняет форму" в очень специфическом, более сильном смысле, чем Functor делает.

Set-это" просто " функтор (не Functor) из подкатегории Hask где Eq является "хорошим" (т. е. подкатегорией, где выполняется конгруэнтность, подстановка). Если бы виды ограничений были вокруг от обратный путь тогда, возможно, set будет Functor какой-то.

Ну, набор можно рассматривать как ковариантный функтор, и как контравариантный функтор; обычно это ковариантный функтор. И для того, чтобы он вел себя относительно равенства, нужно убедиться, что независимо от реализации он делает.

По Поводу Набора.зип - это нонсенс. Как и набор.голова (у вас есть это в Scala). Она не должна существовать.

Comments

    Ничего не найдено.