2016-04-22 5 views
2

Я новичок в Haskell, пытаясь понять Functor. Я взял следующий код из Data.Either module (Замененные данные либо Either1). Я только что обновил, удалив «Either1 a» и заменив его на «Either1» (пример Functor Either1 где).Как создать определение Functor для любого типа

data Either1 a b = Left1 a | Right1 b 

instance Functor Either1 where 
    fmap f (Left1 x) = Left1 x 
    fmap f (Right1 y) = Right1 (f y) 

Когда я пытаюсь загрузить выше фрагмента, я получаю следующую ошибку.

Prelude> :load Sample.hs 
[1 of 1] Compiling Main    (Sample.hs, interpreted) 

Sample.hs:3:18: 
    Expecting one more argument to ‘Either1’ 
    The first argument of ‘Functor’ should have kind ‘* -> *’, 
     but ‘Either1’ has kind ‘* -> * -> *’ 
    In the instance declaration for ‘Functor Either1’ 
Failed, modules loaded: none. 

Мой вопрос, почему я должен поставить «Either1» при определении БПМЖ функции, почему не может «Either1" ?

ответ

2

При реализации Functor, вам нужен тип, который принимает один тип . аргумент Either1 принимает два аргумента типа, a и b, поэтому ваше определение должно выглядеть следующим образом:

instance Functor (Either1 a) where 
    fmap f (Left1 x) = Left1 x 
    fmap f (Right1 y) = Right1 (f y) 

(я исправил ошибку компиляции в Left1 кейс; вы не досчитались параметр f)

Интернета-книга, Learn You a Haskell имеет большое введение в функторах котором говорится в глубине о частичном применении Either описанного здесь.

5

Как указано в ответе на ваш previous question, функторам требуется тип типа * -> *. Either1 имеет вид * -> * -> *; вам необходимо частично применить Either1, чтобы получить тип нужного типа.

> :k Either1 
Either1 :: * -> * -> * 
> :k Either1 Int 
Either1 Int :: * -> * 

При определении экземпляра Functor, это не имеет никакого значения функтора, что первый тип является, таким образом, вы можете просто указать неограниченный переменную типа вместо конкретного типа, как Int.

1

Вы совершенно правы – немного странно, если экземпляр functor работает только по правильному аргументу Either.

Действительно Either не только функтор, это bifunctor:

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

Однако, вы всегда можете рассмотреть бифунктор быть также “ ” monofunctor на одном из двух функторных-аргументов. Из-за того, как работает currying аргументов типа, вы должны выбрать второй в Haskell. По сути, вы работаете с “ половиной bifunctor ”! Например,

type EitherStr = Either String -- the `Right` argument is left open as the functor argument! 

instance Functor EitherStr where 
    fmap _ (Left x) = Left x 
    fmap f (Right y) = Right (f y) 

Но, конечно, это работает для любого данного типа, а не только String. Таким образом, вы можете просто сделать экземпляр родовым по первому аргументу:

∀ a . instance Functor (Either a) where 
    fmap _ (Left x) = Left x 
    fmap f (Right y) = Right (f y) 

Ну, то ∀ a . перед тем экземпляром просто подразумевается в Haskell.Если вы хотите, чтобы сделать его явным это правильный синтаксис:

{-# LANGUAGE ScopedTypeVariables, UnicodeSyntax #-} 
instance ∀ a . Functor (Either a) where 
    fmap _ (Left x) = Left x 
    fmap f (Right y) = Right (f y) 
+0

Aaaaaand, чтобы уточнить, фактический синтаксис для последнего примера - 'instance forall a. Functor (Либо a) где' –

+0

Интересно, так как когда '' 'фактически легально в объявлениях экземпляра? Я почти уверен, что раньше этого не было. – leftaroundabout

+0

Поскольку я помню, но я относительно новичок в Haskell :). Во всяком случае, [live example] (http://coliru.stacked-crooked.com/a/46a9b4be4fd21cab) (неразрешимый бит на самом деле не является его частью). –

1

В Learn You A Haskell, когда вы впервые встретились термин «добрый», вы можете обнаружить, что вам не нужно иметь полное доперли этой концепции, чтобы двигаться дальше , но мне кажется, что вы должны потратить дополнительное время, чтобы выбрать эту идею, потому что дальнейший материал станет все более сложным без хорошего понимания этой проблемы.

Как уже упоминалось выше, любой из них имеет вид * -> * -> *, но Functor ожидает чего-то с видом * -> * - вы можете думать об этом как о типе, параметризованном с помощью другого (конкретного) типа - конструктора типа фактического типа. Например, Int имеет вид *, Maybe Int имеет вид *, но Maybe имеет вид * -> * - поэтому тип конструктора Maybe является хорошим кандидатом для использования в Functor. Как мы можем достичь такого же качества с помощью Either a b - снова используйте частичное приложение, чтобы исправить первый параметр, и вы получите Either a - конструкция, которая параметризована одним конкретным типом b. Здесь вы должны увидеть, почему первый параметр не изменяется в fmap функция - couse исправлена.

Прошу прощения за возможное дублирование первого ответа, но я помню, что тратил слишком много времени и пытался раскрыть kinds - так что мне срочно нужен был кто-нибудь, чтобы дать мне простое объяснение.

Удачи вам!

Смежные вопросы