Как превратить Либо в функтор второго типа - PullRequest
0 голосов
/ 09 мая 2018

Я читал "Учим тебя на Хаскеле за великое благо!" и прямо сейчас я нахожусь в разделе «Класс типов Functor».

Здесь они превращают Either в функтор, исправляя первый тип следующим образом:

instance Functor (Either a) where
  fmap f (Right x) = Right (f x)
  fmap f (Left x)  = Left x

Итак, я хотел спросить, как я могу вместо этого сделать Either в функтор первого типа (исправив второй тип), чтобы я получил следующее определение для fmap

fmap f (Left x) = Left (f x)
fmap f (Right x)  = Right x

1 Ответ

0 голосов
/ 09 мая 2018

Вы не можете;Either a может быть функтором, потому что частичное применение Either имеет вид * -> *, но вы не можете сделать частичное применение справа.

Вместо этого вас может заинтересовать Bifunctor экземпляр Either:

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

bimap принимает две функции, по одной для каждого из двух типов, заключенных в Either.

> bimap (+1) length (Left 3)
Left 4
> bimap (+1) length (Right "hi")
Right 2

Также есть функции first и second, которые фокусируются на одном или другом типе.second соответствует обычному fmap для Either;first - это функция, которую вы ищете.

> first (+1) (Left 3)
Left 4
> first (+1) (Right 3)
Right 3
> second (+1) (Left 3)
Left 3
> second (+1) (Right 3)
Right 4

(отзыв на @leftaroundabout)

Модуль Control.Arrow обеспечивает функцию left, которая эффективнотакой же, как second, но с более описательным именем и другим производным.Сравните их типы:

> :t Data.Bifunctor.second
Data.Bifunctor.second :: Bifunctor p => (b -> c) -> p a b -> p a c
> :t Control.Arrow.left
Control.Arrow.left :: ArrowChoice a => a b c -> a (Either b d) (Either c d)

second жестко запрограммирован для работы с функциями и может быть ограничен p ~ Either.left жестко запрограммирован для работы с Either и может быть ограничен a ~ (->).


Смущает, что Control.Arrow также обеспечивает функцию second, котораяаналогичен экземпляру Bifunctor для кортежей:

> :t Control.Arrow.second
Control.Arrow.second :: Arrow a => a b c -> a (d, b) (d, c)

> Control.Arrow.second (+1) (1,2) == Data.Bifunctor.second (+1) (1,2)
True
...