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

Понимание того, как каждый из них является экземпляром Functor

В свободное время я изучаю Haskell, так что это вопрос начинающих.

В моих чтениях я наткнулся на пример, иллюстрирующий, как Either a создается экземпляр Functor:

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

Теперь я пытаюсь понять, почему карты реализации в случае конструктора значений Right, но не в случае Left?

Вот мое понимание:

Сначала позвольте мне переписать вышеупомянутый экземпляр как

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

Сейчас:

  • Я знаю, что fmap :: (c -> d) -> f c -> f d

  • если подставить f с помощью Either a, получим fmap :: (c -> d) -> Either a c -> Either a d

  • тип Right (g x) равен Either a (g x), а тип g x равен d, поэтому мы имеем тип Right (g x) Either a d, что мы ожидаем от fmap (см. выше)

  • теперь, если мы посмотрим на Left (g x), мы можем использовать те же рассуждения, что сказать, что его тип Either (g x) b, то есть Either d b, чего мы не ожидаем от fmap (см. 2 выше): d должен быть вторым параметром, а не первым! Таким образом, мы не можем сопоставлять Left.

Правильны ли мои рассуждения?

4b9b3361

Ответ 1

Это правильно. Существует еще одна довольно важная причина такого поведения: вы можете думать о Either a b как о вычислении, которое может преуспеть и вернуть b или сбой с сообщением об ошибке a. (Это также, как работает экземпляр монады). Поэтому естественным является то, что экземпляр functor не будет касаться значений Left, так как вы хотите сопоставить вычисление, если оно терпит неудачу, нечего манипулировать.

Ответ 2

Ваша учетная запись права, конечно. Возможно, причина, по которой мы сталкиваемся с такими случаями, состоит в том, что мы действительно определяем бесконечно много экземпляров-исполнителей сразу - по одному для каждого возможного типа Left. Но экземпляр Functor - это систематический способ работы с бесконечным числом типов в системе. Поэтому мы определяем бесконечно много способов систематического управления бесконечным числом типов в системе. Экземпляр включает в себя общность двумя способами.

Если взять его поэтапно, возможно, это не так странно. Первым из этих типов является longwinded версия Maybe, использующая тип устройства () и его единственное допустимое значение ():

data MightBe b     = Nope ()    | Yep b
data UnlessError b = Bad String | Good b
data ElseInt b     = Else Int   | Value b

Здесь мы можем устать и сделать абстракцию:

data Unless a b    = Mere a     | Genuine b

Теперь мы делаем наши экземпляры Functor, без проблем, первый, очень похожий на экземпляр для Maybe:

instance Functor MightBe where
  fmap f (Nope ()) = Nope ()   -- compare with Nothing
  fmap f (Yep x)   = Yep (f x) -- compare with Just (f x)

instance Functor UnlessError where
  fmap f (Bad str) = Bad str   -- a more informative Nothing
  fmap f (Good x)  = Good (f x)

instance Functor ElseInt where
  fmap f (Else n) = Else n 
  fmap f (Value b) = Value (f b)

Но, опять же, зачем беспокоиться, сделайте абстракцию:

instance Functor (Unless a) where
  fmap f (Mere a) = Mere a
  fmap f (Genuine x) = Genuine (f x)

Термины Mere a не затрагиваются, поскольку значения (), String и Int не были затронуты.

Ответ 3

Как уже упоминалось, тип Either является функтором в обоих его аргументах. Но в Haskell мы можем (напрямую) определять только функторы в аргументах типа типа. В таких случаях мы можем обойти ограничение, используя newtype s:

newtype FlipEither b a = FlipEither { unFlipEither :: Either a b }

Итак, у нас есть конструктор FlipEither :: Either a b -> FlipEither b a, который обертывает Either в наш newtype с переменными аргументами типа. И у нас есть dectructor unFlipEither :: FlipEither b a -> Either a b, который разворачивает его обратно. Теперь мы можем определить экземпляр functor в FlipEither последнем аргументе, который на самом деле является Either первым аргументом:

instance Functor (FlipEither b) where
    fmap f (FlipEither (Left x))  = FlipEither (Left (f x))
    fmap f (FlipEither (Right x)) = FlipEither (Right x)

Заметим, что если мы забудем FlipEither какое-то время, мы получим просто определение Functor для Either, только с Left/Right сменили. И теперь, когда нам нужен экземпляр Functor в аргументе Either первого типа, мы можем обернуть это значение в FlipEither и развернуть его позже. Например:

fmapE2 :: (a -> b) -> Either a c -> Either b c
fmapE2 f = unFlipEither . fmap f . FlipEither

Обновление: Посмотрите Data.Bifunctor, из которых Either и (,) являются экземплярами из. Каждый bifunctor имеет два аргумента и является функтором в каждом из них. Это отражено в методах Bifunctor first и second.

Определение Bifunctor of Either очень симметрично:

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

    first  f = bimap f id

    second f = bimap id f

Ответ 4

Теперь я пытаюсь понять, почему карты реализации в случае Конструктор правого значения, но не в случае левого?

Подключите сюда, и это может иметь смысл.

Предположим, что a = String (сообщение об ошибке) Вы применяете либо a, либо float.

Итак, у вас есть f: Float → Integer, например, округление.

(Либо String) (Float) = Либо String Float.

now (fmap f):: Либо String Float → Либо String Int Итак, что вы собираетесь делать с f? f не имеет понятия, что делать со строками, чтобы вы ничего не могли сделать. Это , очевидно,, единственное, на что вы можете воздействовать, - это правильные значения, оставляя неизменным оставленные значения.

Другими словами, либо a является функтором, потому что существует такая очевидная fmap, заданная следующим образом:

  • для правых значений применяется f
  • для значений Left ничего не делает