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)

Конечно, подход, основанный на функции типа, выглядит лучше и, вероятно, предпочтительнее.

Другие вопросы по тегам