2

我正在尝试使用类型类和函数依赖项来获取一个类型函数,该函数可以转换IntCont 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)

鉴于TypeConvtypeclass 有一个我读为:“给定reprand a,我们可以推断b”并为 提供了一个实例Int,为什么 ghc 不能推断a' ~ Cont Int呢?

4

2 回答 2

6

如果您想要一个类型函数,请使用 Type Families - 这就是它们的用途。类型家庭很容易,做你所期望的。

通常编译器没有推断出你的类型的原因是你指定了一个函数依赖(逻辑关系)而不是一个函数(计算工具)。众所周知,使用fundeps 是违反直觉的,部分原因是您在类型级别进行逻辑编程,同时在值级别进行函数式编程。转变!使用类型级别的函数,以及可爱的类型族扩展。随附免费的 lambda 冰箱磁铁,只有四个令牌(不包括 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"]
于 2012-09-25T23:21:57.750 回答
2

Andrew 不必要地批评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)

当然,基于类型函数的方法确实看起来更好,并且可能更可取。

于 2012-09-26T03:46:42.560 回答