Вырожденное объявление экземпляра класса типов с использованием постоянного значения

Я сократил все до самого необходимого, так что терпите меня, если приведенный ниже пример кода является надуманным. Допустим, у нас есть:

class Foo a where
    foo :: a

data Type a = Type a

instance (Foo a) => Foo (Type a) where
    foo = Type foo

Теперь предположим, что я хочу сделать Type a экземпляром, скажем, Show всякий раз, когда a является экземпляром как Foo, так и Show (Show был выбран, чтобы избежать определения другого класса типов). Итак, как мы хотим, чтобы Type a был экземпляром Show? Ну, если мы не сумасшедшие, мы, конечно, хотели бы, чтобы это было что-то вроде

instance (Foo a, Show a) => Show (Type a) where
    show (Type x) = show x

или, может быть

instance (Foo a, Show a) => Show (Type a) where
    show (Type x) = "Blabla " ++ (show x)

Это все здорово и отлично работает. По какой-то необъяснимой причине мы хотели бы, чтобы show выводил то, что foo :: a выглядит / отображается! В нашей надуманной обстановке я не могу представить, зачем нам это нужно, но допустим, что мы хотим. Не должен

instance (Foo a, Show a) => Show (Type a) where
    show _ = show foo

Покажи фокус?

Увы, GHC говорит

Переменная неоднозначного типа 'a' в ограничениях: 'Foo a' [...] 'Show a'

Может быть, GHC не может понять, о каком foo я говорю. Я имею в виду foo :: Type a или foo :: a? Изменение предыдущего фрагмента на

instance (Foo a, Show a) => Show (Type a) where
    show _ = show (foo :: a)

дает мне

Не удалось вывести (Foo a1) из context (), возникающего из-за использования 'foo' в [...] Возможное исправление: добавить (Foo a1) в контекст сигнатуры типа выражения В первом аргументе 'show' , а именно '(foo :: a)' в выражении: show (foo :: a)

В этот момент я начинаю думать, что неправильно понял кое-что. Тем не менее, у меня есть странное ощущение, что подобные конструкции работали для меня в прошлом.


person gspr    schedule 28.10.2010    source источник


Ответы (1)


Я думаю, что проблема в том, что переменные типа не привязаны к определениям. То есть в

instance (Foo a, Show a) => Show (Type a) where
    show _ = show (foo :: a)

a во второй строке отличается от a в первой строке, поэтому в сообщении об ошибке он отображается как a1. См. http://www.haskell.org/haskellwiki/Scoped_type_variables. Если это проблема, это должно сработать (у меня на этой машине нет GHC):

asTypeOf :: a -> a -> a
asTypeOf a b = a

instance (Foo a, Show a) => Show (Type a) where
    show (Type x) = show (foo `asTypeOf` x)
person Alexey Romanov    schedule 28.10.2010
comment
Хороший ответ, за исключением небольшой опечатки, ведущей к незавершению: последняя строка должна быть show (Type x) = show (foo `asTypeOf` x). Вы также можете использовать {-# LANGUAGE ScopedTypeVariables #-} вместе с show _ = show (foo :: a) - person Tom Crockett; 28.10.2010
comment
Большой! Спасибо, это действительно прояснило ситуацию. Также спасибо pelotom за указание на расширение GHC. - person gspr; 28.10.2010
comment
Причина, по которой я не упомянул расширение, заключается в том, что оно требует, чтобы forall был написан явно: haskell.org/ghc/docs/6.12.2/html/users_guide/, но я не был уверен, куда он должен идти: instance forall a. (Foo a, Show a) => Show (Type a)? Где-нибудь еще? Ненужно для экземпляров? - person Alexey Romanov; 28.10.2010
comment
Алексей: По крайней мере, forall мне не понадобилось. В моем примере я включил расширение GHC и реализовал show как show _ = show (foo :: a). Работает отлично. Также приятно узнать о вашем asTypeOf подходе. - person gspr; 28.10.2010
comment
По крайней мере, мне не нужно forall. Хорошо знать! - person Alexey Romanov; 28.10.2010