Почему я не могу создать экземпляр с этим непараметризованным типом?

Почему этот код правильный

instance Functor IO where -- note that IO isn't parametrized, and it's correct
    fmap f action = do
        result <- action
        return (f result)

но следующий код имеет ошибку компилятора?

class Print a where
    print :: a -> String

data A t = A t
instance Print A where  -- error: expecting one more argument to `A'
    print a = "abc"

person Jofsey    schedule 25.10.2012    source источник


Ответы (3)


Это связано с тем, что виды не совпадают. Обычные типы имеют вид *, а конструкторы типов, такие как A или IO, имеют тип * -> *, что указывает на то, что им нужен параметр типа для возврата типа.

В определении класса Print компилятор делает вывод, что, поскольку a используется как простой тип, он должен иметь тип *. Однако Functor работает с конструкторами типов вида * -> *:

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

Здесь f используется не как простой тип, а как конструктор типа, поэтому его предполагаемый тип — * -> *. Вы можете проверить это с помощью команды :kind в GHCi:

> :kind Print
Print :: * -> Constraint
> :kind Functor
Functor :: (* -> *) -> Constraint 
person hammar    schedule 25.10.2012

Когда ты говоришь

class Print a where
   print' :: a -> String

Вы убедитесь, что a должен быть типом, но когда вы говорите

data A t = A t

вы делаете A конструктором типа - A не является типом, а A Int, например. A — это своего рода функция для типов, но a в классе Print должно быть значением типа, а не функцией типа.

Вы могли бы сделать

instance Print (A Int) where
  print' a = "abc"

Это нормально для IO, потому что класс Functor запрашивает конструктор типа.

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

Вы можете видеть, что, поскольку f a — это тип, f — это конструктор типа, точно так же, как IO и A. Вы сможете сделать

instance Functor A where  -- OK, A is a constructor, Functor needs one
  fmap f (A x) = A (f x)

и ты не сможешь сделать

instance Eq IO where -- not OK - IO is a constructor and Eq needs a type
    (==) = error "this code won't compile"

(Я использовал print' вместо print, чтобы избежать конфликта со стандартной функцией print.)

person AndrewC    schedule 25.10.2012

Попробуйте мысленно (или с помощью текстового редактора) заполнить типы, указанные в определении класса, типом, который вы использовали в экземпляре.

Из:

class Print a where
    print :: a -> String

а также

data A t = A t

мы хотим

instance Print A

Итак, заменив a в определении класса типа на A, которое мы называем экземпляром, мы получим следующее:

class Print A where
    print :: A -> String

О-о. A -> String не имеет смысла как тип, поскольку стрелка типа функции принимает тип слева и тип справа и дает вам тип функции. Но A не является типом, так как вы объявили A с data A t; A t — это тип для любого типа t, но A — это конструктор типа. Он может создать тип, если вы примените его к типу, но сам A представляет собой нечто иное. Таким образом, вы можете сделать A t экземпляром Print, но не самим A.

Так почему же instance Functor IO сработало? Давайте посмотрим на определение класса:

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

Теперь давайте попробуем заменить IO на f:

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

В конечном итоге IOs применяются к параметрам типа, так что все работает. Здесь мы столкнулись бы с проблемами, если бы попытались сделать конкретный тип, такой как Int или A t, экземпляром Functor.

person Ben    schedule 26.10.2012