Управление порядком аргументов ввести конструкторов

Я записал что-то вроде этого:

instance Functor (Either e) where

   fmap _ (Left a) = Left a

   fmap f (Right b) = Right (f b)

Как я делаю то же, если я хочу fmap изменить значение, только если это Left?

Я имею в виду, какой синтаксис делают я использую, чтобы указать, что я использую тип Either _ b вместо Either a _?

13
задан Don Stewart 25 February 2010 в 18:36
поделиться

3 ответа

К сожалению, я не думаю, что есть способ сделать это напрямую. С функцией вы можете использовать flip для частичного применения второго аргумента, но это не работает с конструкторами типов, такими как Either .

Самым простым, вероятно, является обертывание его в newtype :

newtype Mirror b a = Mirrored (Either a b)

instance Functor (Mirror e) where
    fmap _ (Mirrored (Right a)) = Mirrored $ Right a
    fmap f (Mirrored (Left b)) = Mirrored $ Left (f b)

Обертывание с newtype также является стандартным способом создания нескольких экземпляров для одного типа, например Sum и Произведение являются экземплярами Monoid для числовых типов. В противном случае у вас может быть только один экземпляр для каждого типа.

Кроме того, в зависимости от того, что вы хотите сделать, можно также проигнорировать Функтор и определить свой собственный класс типа следующим образом:

class Bifunctor f where
    bimap :: (a -> c) -> (b -> d) -> f a b -> f c d

instance Bifunctor Either where
    bimap f _ (Left a)  = Left  $ f a
    bimap _ g (Right b) = Right $ g b

instance Bifunctor (,) where
    bimap f g (a, b) = (f a, g b)

Очевидно, что этот класс вдвое веселее, чем обычный Функтор . Конечно, вы не сможете сделать из этого экземпляр Monad очень просто.

9
ответ дан 2 December 2019 в 00:03
поделиться

По сути, вам нужен комбинатор 'flip' для типов. Обертка newtype, которая инвертирует порядок, должна работать, как говорит camccann. Обратите внимание, что вы не можете использовать синоним 'type', так как они могут быть частично неприменимы.

3
ответ дан 2 December 2019 в 00:03
поделиться

Вы не можете создать экземпляр, который ищете, напрямую.

Для работы вывода типов и классов типов существует определенная позиционная предвзятость в упорядочении аргументов в типах.Было показано, что если мы позволим произвольно переупорядочить аргументы при создании экземпляров классов типов, этот вывод типа станет неразрешимым.

Вы можете использовать класс Bifunctor , который может отображать оба аргумента по отдельности.

class Bifunctor f where
    bimap :: (a -> b) -> (c -> d) -> f a c -> f b d
    first :: (a -> b) -> f a c -> f b c
    second :: (c -> d) -> f a c -> f a d

    first f = bimap f id
    second = bimap id

instance Bifunctor Either where
    bimap f _ (Left a) = Left (f a)
    bimap _ g (Right b) = Right (g b)

instance Bifunctor (,) where
    bimap f g (a,b) = (f a, g b)

Или вы можете использовать комбинатор Flip , например:

newtype Flip f a b = Flip { unFlip :: f b a }

Обобщенные версии обоих из них доступны в дополнительных категориях при взломе. Последний даже включает экземпляр для Functor (Flip Either a) , потому что Either является Bifunctor . (Я, вероятно, должен исправить это, чтобы потребовать только PFunctor )

В конечном счете, порядок аргументов в конструкторе типа важен при определении того, какие классы вы можете создать. Возможно, вам придется использовать оболочки нового типа (например, Flip выше), чтобы поместить аргументы там, где они должны быть, чтобы соответствовать требованиям для создания экземпляра другого класса типов. Это цена, которую мы платим за вывод ограничений типа-класса.

4
ответ дан 2 December 2019 в 00:03
поделиться
Другие вопросы по тегам:

Похожие вопросы: