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

Пример типа с видом * → *, который не может быть экземпляром Functor

Я очень новичок Haskell, поэтому извиняюсь, если ответ очевиден, но я работаю через Typeclassopedia, чтобы лучше понять категории. При выполнении упражнений для раздела "Функторы" я столкнулся с этой проблемой:

Приведите пример типа вида * → *, который нельзя сделать экземпляром   Functor (без использования undefined).

Моя первая мысль заключалась в том, чтобы определить какое-то бесконечно рекурсивное определение fmap, но не было бы, по сути, таким же, как если бы в определении было undefined?

Если кто-то может объяснить ответ, мы будем очень благодарны.

Спасибо!

Источник оригинального упражнения здесь, раздел 3: http://www.haskell.org/haskellwiki/Typeclassopedia#Introduction

4b9b3361

Ответ 1

Простым примером является

data K a = K (a -> Int)

Здесь, о котором говорит ghci, мы пытаемся автоматически получить экземпляр Functor для K:

Prelude> :set -XDeriveFunctor
Prelude> data K a = K (a -> Int)
Prelude> :k K
K :: * -> *
Prelude> data K a = K (a -> Int) deriving Functor

<interactive>:14:34:
    Can't make a derived instance of `Functor K':
      Constructor `K' must not use the type variable in a function argument
    In the data type declaration for `K'

Проблема состоит в том, что стандартный класс Functor фактически представляет ковариантные функторы (fmap поднимает свой аргумент до f a -> f b), но вы не можете создать a -> b и a -> Int, чтобы получить функцию тип b -> Int (см. ответ Рамона). Тем не менее, можно определить класс типа для контравариантных функторов:

class Contravariant f where
    contramap :: (a -> b) -> f b -> f a 

и сделайте K его экземпляр:

instance Contravariant K where
    contramap f (K g) = K (g . f)

Подробнее о ковариации/контравариантности в Haskell см. здесь.

Изменить: Здесь также хороший комментарий по этому вопросу от Криса Смита на Reddit.

Ответ 2

Расширить мой (короткий) комментарий и ответить на вопрос Михаила:

Учитывая (-> Int), вы ожидаете, что fmap будет выглядеть следующим образом:

(a -> Int) -> (a -> b) -> (b -> Int)

или

(a -> Int) -> (a -> b) -> b -> Int

Нетрудно доказать, что из трех аргументов (a -> Int), (a -> b), b нет возможного пути достижения Int (без undefined), поэтому из (a -> Int), (a -> b) невозможно достичь (b -> Int). Вывод: экземпляр Functor существует для (-> Int).