Haskell: Почему GHC не выводит тип в этом классе типов с помощью fundeps?
Я пытаюсь использовать классы типов и функциональные зависимости, чтобы получить функцию типа, которая может преобразовать, скажем, Int
в Cont Int
в приведенном ниже коде используйте его в другом классе типов, как показано ниже.
{-# LANGUAGE KindSignatures, FunctionalDependencies, FlexibleInstances, FlexibleContexts #-}
newtype TestData a b = TestData b
newtype Cont a = Cont a
class TypeConv (repr :: * -> *) a b | repr a -> b where
class Lift repr a where
liftOp :: (TypeConv repr a a') => a -> repr a'
instance TypeConv (TestData a) Int (Cont Int) where
instance Lift (TestData a) Int where
liftOp i = TestData (Cont i)
А вот и ошибка от ghci 7.4.2
src/Test.hs:13:26:
Could not deduce (a' ~ Cont Int)
from the context (Full (TestData a) Int a')
bound by the type signature for
liftOp :: Full (TestData a) Int a' => Int -> TestData a a'
at src/Test.hs:13:5-32
a' is a rigid type variable bound by
the type signature for
liftOp :: Full (TestData a) Int a' => Int -> TestData a a'
at src/Test.hs:13:5
In the return type of a call of `Cont'
In the first argument of `TestData', namely `(Cont i)'
In the expression: TestData (Cont i)
Учитывая, что TypeConv
у класса типов есть фундеп, который я читаю как repr
а также a
мы можем сделать вывод b
"и предоставил экземпляр для Int
почему GHC не может сделать вывод, что a' ~ Cont Int
?
2 ответа
Если вам нужна функция типа, используйте Type Families - вот для чего они. Тип Семьи легки и делают то, что вы ожидаете.
Часто причиной того, что компилятор не определил ваш тип, является то, что вы указали функциональную зависимость (логические отношения), а не функцию (инструмент вычисления). Использование fundeps заведомо нелогично, отчасти потому, что вы выполняете логическое программирование на уровне типов, а функциональное программирование на уровне значений. Переключатель! Используйте функции на уровне типа с прекрасным расширением семейства типов. Поставляется с бесплатным лямбда-магнитом на холодильник с четырьмя жетонами (P & P не входит в комплект).
Я не уверен, чего вы пытались достичь, но вот пример - поправьте меня, если я иду в неправильном направлении. Тебе понадобиться
{-# LANGUAGE TypeFamilies #-}
Затем мы можем определить класс, который включает синоним локального типа, TypeConv
которая является нашей функцией типа:
class Lift a where
type TypeConv a
liftOp :: a -> TypeConv a
И тогда мы могли бы сделать пример
instance Lift Int where
type TypeConv Int = TestData (Cont Int)
liftOp i = TestData (Cont i)
и если мы просто хотим обернуть Cont
мы могли бы сделать
instance Lift Integer where
type TypeConv Integer = Cont Integer
liftOp i = Cont i
и вы можете сойти с ума с
instance Lift Char where
type TypeConv Char = [String]
liftOp c = replicate 4 (replicate 5 c)
который позволяет вам иметь
*Main> liftOp (5::Int)
TestData (Cont 5)
*Main> liftOp (5::Integer)
Cont 5
*Main> liftOp '5'
["55555","55555","55555","55555"]
Эндрю излишне критически относится к fundeps
Конечно, функции типов проще, но функциональные зависимости часто обеспечивают дополнительную гибкость. В этом случае вы просто должны принять более длинные определения классов
{-# LANGUAGE KindSignatures,
FunctionalDependencies,
FlexibleInstances,
FlexibleContexts #-}
newtype TestData a b = TestData b
newtype Cont a = Cont a
class TypeConv (repr :: * -> *) a b | repr a -> b
class TypeConv repr a b => Lift repr a b | repr a -> b where
liftOp :: a -> repr b
instance TypeConv (TestData a) Int (Cont Int)
instance Lift (TestData a) Int (Cont Int) where
liftOp i = TestData (Cont i)
Конечно, подход, основанный на функции типа, выглядит лучше и, вероятно, предпочтительнее.