Неявные параметры типа в определении класса Haskell? - PullRequest
4 голосов
/ 14 июля 2010

Обычно кажется, что следующее недопустимо:

class Foo a where
    foo :: a -> b -> a

Что имеет смысл; откуда мы знаем, что такое b?

Однако, если мы посмотрим на определение Функтора:

class Functor f where
    fmap :: (a -> b) -> f a -> f b

мы видим a и b, даже если мы указываем f в качестве переменной типа. Я предполагаю, что это разрешено, потому что компилятор видит, например, f a и может выяснить, что f сам по себе должен принимать a, поэтому безопасно использовать этот a в другом месте в нашем определении Functor. Я прав?

Ответы [ 2 ]

6 голосов
/ 14 июля 2010

Давайте посмотрим на каждую строку отдельно.

class Functor f where

Здесь объявляется однопараметрический класс типа Functor;тип, который его удовлетворяет, будет называться f.

  fmap :: (a -> b) -> f a -> f b

Как и любое определение функции, все переменные свободного типа неявно forall ed - их можно заменить на что угодно.Однако, благодаря первой строке, f находится в области видимости.Таким образом, fmap имеет сигнатуру типа fmap :: forall a b. Functor f => (a -> b) -> f a -> f b.Другими словами, каждый функтор должен иметь определение fmap, которое может работать для любых a и b, а f должно иметь kind (типтипа) * -> *;то есть это должен быть тип, который принимает другой тип, такой как [] или Maybe или IO.

То, что вы сказали, неверно;a не является особенным, и если бы у нас была другая функция в Functor, она бы не увидела тот же a или b.Однако компилятор использует бит f a, чтобы выяснить, каким должен быть тип f.Кроме того, ваш Foo класс совершенно легален;Я мог бы указать экземпляр следующим образом:

instance Foo (a -> b) where
  foo f _ = f

Это удовлетворяет foo :: a -> b -> a для any b;обратите внимание, что b в Foo (a -> b) отличается.По общему признанию, это не очень интересный случай, но это совершенно законно.

3 голосов
/ 14 июля 2010

Это не нужно "знать". Это просто необходимо для проверки типов (то есть не провалить проверку типов). b может быть чем угодно; и функция foo должна иметь возможность принимать любой тип в качестве второго параметра.

Рассмотрим функцию const из Prelude:

const            :: a -> b -> a
const x _        =  x

Как он "знает", что такое b (или a, в этом отношении)?

...