"Недопустимый полиморфный или квалифицированный тип" в объявлении экземпляра (деревья стиля System-F)
Я экспериментирую с реализацией структур данных в стиле System-F в Haskell.
Я буду использовать A <B>
означать применение термина A
к типу B
просто, чтобы сделать это однозначным (также используя заглавные буквы для типов).
Скажем Tree <T>
тип бинарных деревьев со значениями типа T
, Мы хотим найти тип, который может действовать как Tree <T>
, Есть три конструктора:
EmptyTree <T> : (Tree <T>)
Leaf <T> : T → (Tree <T>)
Branch <T> : (Tree <T>) → (Tree <T>) → (Tree <T>)
Так что, по некоторым соображениям, я думаю, благодаря Жирару, мы можем использовать следующее
Tree T = ∀ A. A → (T → A) → (A → A → A) → A
из которого
Empty <T>
= ΛA.λa:A.λf:(T → A).λg:(A → A → A).
a
Leaf <T> (x:T)
= ΛA.λa:A.λf:(T → A).λg:(A → A → A).
f x
Branch <T> (t1:Tree <T>) (t2:Tree <T>)
= ΛA.λa:A.λf:(T → A).λg:(A → A → A).
g (t1 <A> a f g) (t2 <A> a f g)
Я узнал о директивах, необходимых для этих вещей в Хаскеле, и я не думаю, что что-то пропустил. Итак, в Haskell:
{-# LANGUAGE RankNTypes #-}
type T t = forall a.a -> (t -> a) -> (a -> a -> a) -> a
empty :: T t
empty = \a _ _ -> a
leaf :: t -> T t
leaf x = \_ f _ -> f x
fork :: T t -> T t -> T t
fork t1 t2 = \a f g -> g (t1 a f g) (t2 a f g)
Пока что все это компилируется и, похоже, работает. Проблема возникает, когда я пытаюсь сделать экземпляр для Show
для меня T t
тип. Я добавил больше директив:
{-# LANGUAGE RankNTypes, TypeSynonymInstances, FlexibleInstances #-}
и функция для печати дерева
displayTree :: Show t => T t -> String
displayTree t = t displayEmpty show displayFork
с соответствующими помощниками displayEmpty :: String
а также displayFork :: String -> String -> String
, Это также компилирует и работает (вплоть до красивости). Теперь, если я попытаюсь создать экземпляр T t
как пример Show
instance Show t => Show (T t) where
show = displayTree
Я получаю следующую ошибку при попытке компиляции:
Illegal polymorphic or qualified type: T t
In the instance declaration for 'Show (T t)'
Я (думаю, я) понимаю необходимость TypeSynonymInstances
а также FlexibleInstances
и прагматические причины их существования, но я не понимаю, почему мой тип T t
до сих пор не может быть объявлен экземпляром Show
, Есть ли способ сделать это, и какое свойство T t
означает, что это в настоящее время проблематично в моем коде?
1 ответ
Во-первых, есть хитрость, которая позволяет вам написать несколько экземпляров для универсально квантифицированных типов: мы удаляем forall
-s из главы экземпляра, и ввести ограничения на равенство типов для любых типов, которые мы хотим создать для переменных. В нашем случае:
{-# LANGUAGE RankNTypes, FlexibleInstances, TypeFamilies #-}
instance (a ~ String, Show t) => Show (a -> (t -> a) -> (a -> a -> a) -> a) where
show t = t "nil" show (\l r -> "(" ++ l ++ ", " ++ r ++ ")")
-- show (fork empty (fork (leaf ()) empty)) == "(nil, ((), nil))"
Это работает, потому что глава экземпляра неявно количественно определяет переменную для нас. Конечно, если мы хотим создать полиморфный тип для нескольких различных типов, этот прием может быть неприменим.
С другой стороны, если бы GHC допускал полиморфные типы в экземплярах, это было бы бесполезно, потому что GHC не поддерживает неумышленную реализацию (и ImpredicativeTypes
прагма на самом деле не работает). Например, Show (forall a. t)
не подразумевает Show [forall a. t]
, так как [forall a. t]
не действует с самого начала. Кроме того, ограничения классов создаются в той же системе, что и обычные конструкторы типов (за исключением того, что они возвращаются в виде Constraint
), так Show (forall a. t)
аналогичным образом недействительным без неумышленного создания.