0

我有一个数据类型,它带有'隐藏'(推断)类型和具体值。现在我尝试实现一个可以改变这两种功能的功能,但无法通过GHC。变换参数化类型(带有推断参数)

我的示例代码是这样的:

data T tag val = T val 

data A = A 
data B = B 

mkIntVal :: T a b -> T Int b 
mkIntVal (T x) = T x 

mkCharVal :: T a b -> T Char b 
mkCharVal (T x) = T x 

convert :: T Int a -> T Char b 
convert (T A) = mkCharVal $ T B 
convert (T B) = mkCharVal $ T A 

它产生的错误是这样的:

test.hs:13:12: 
    Couldn't match type `A' with `B' 
    In the pattern: A 
    In the pattern: T A 
    In an equation for `convert': convert (T A) = mkCharVal $ T B 

test.hs:13:17: 
    Couldn't match type `B' with `A' 
    Expected type: T Char b 
     Actual type: T Char B 
    In the expression: mkCharVal $ T B 
    In an equation for `convert': convert (T A) = mkCharVal $ T B 

了什么工作要做,以使这项工作?我必须更改数据结构吗?


编辑

我试图延长Don Stewart的解决方案与多态数据类型的工作。我一直在玩弄实例定义,但最有前途的期待与想出了是这样的:

{-# LANGUAGE MultiParamTypeClasses #-} 
{-# LANGUAGE FunctionalDependencies #-} 
{-# LANGUAGE FlexibleInstances #-} 
{-# LANGUAGE FlexibleContexts #-} 

data C a = C a deriving Show 

class Convertable inVal outVal outTag | outVal -> outTag where 
    convert :: T Int inVal -> T outTag outVal 

instance Convertable A B Char where 
    convert (T A) = mkCharVal $ T B 

instance Convertable B A Char where 
    convert (T B) = mkCharVal $ T A 

instance Convertable a b Char => Convertable (C a) (C (T Char b)) Char where 
    convert (T (C val)) = mkCharVal $ T (C (convert val)) -- line 29 

但是这给了我又一个错误信息:

test.hs:29:57: 
    Could not deduce (a ~ T Int inVal0) 
    from the context (Convertable a b Char) 
     bound by the instance declaration at test.hs:28:10-70 
     `a' is a rigid type variable bound by 
      the instance declaration at test.hs:28:22 
    In the first argument of `convert', namely `val' 
    In the first argument of `C', namely `(convert val)' 
    In the first argument of `T', namely `(C (convert val))' 

正如唐说,这应该是可能我对这将如何实施感兴趣。


解决方案

多了很多“玩”后,我终于想出了一些作品。这对你看起来不错吗?

{-# LANGUAGE MultiParamTypeClasses #-} 
{-# LANGUAGE FunctionalDependencies #-} 
{-# LANGUAGE FlexibleContexts #-} 
{-# LANGUAGE FlexibleInstances #-} 
{-# LANGUAGE UndecidableInstances #-} 
{-# LANGUAGE OverlappingInstances #-} 


data T tag val = T val deriving Show 

data A = A deriving Show 
data B = B deriving Show 
data C a = C a deriving Show 


class Convertable inTag inVal outTag outVal | inTag -> outTag, inVal -> outVal 
where 
    convert :: T inTag inVal -> T outTag outVal 

instance Convertable Int A Char B where 
    convert (T A) = T B 

instance Convertable Int B Char A where 
    convert (T B) = T A 

instance (Convertable Int (T Int a) Char (T Char b), Convertable Int a Char b) 
    => Convertable Int (C (T Int a)) Char (C (T Char b)) where 
    convert (T (C x)) = T (C (convert x)) 

instance Convertable Int (C (T Int A)) Char (C (T Char B)) where 
    convert (T (C x)) = T (C (convert x)) 

instance Convertable Int (C (T Int B)) Char (C (T Char A)) where 
    convert (T (C x)) = T (C (convert x)) 

用法:

*Main> convert $ mkIntVal $ T $ C $ mkIntVal $ T A 
T (C (T B)) 
*Main> :t it 
it :: T Char (C (T Char B)) 

回答

1

convert功能的每个案例都有一个不同的,相互冲突的类型:

convertA :: T t A -> T Char B 
convertA (T A) = mkCharVal $ T B 

convertB :: T t B -> T Char A 
convertB (T B) = mkCharVal $ T A 

您可以通过一个类型类统一这些,

{-# LANGUAGE MultiParamTypeClasses #-} 
{-# LANGUAGE FunctionalDependencies #-} 


class C a b c | b -> c where 
    convert :: T t a -> T c b 

instance C A B Char where 
    convert (T A) = mkCharVal (T B) 

instance C B A Char where 
    convert (T B) = mkCharVal (T A) 

如果你真的希望在不同类型的单一功能下,向不同的方向转换。请注意,这是如何将带有标签的任何T放弃它,并用由值类型确定的新标签替换标签和值。

+0

现在我明白我的尝试出了什么问题。稍后有可能将此解决方案扩展为多态结果类型,还是仅限于“正常”类型?因为我没有任何具体的值放入实例定义中,所以我不知道如何去执行像'convert(T(C val))= mkCharVal $ T(C(convert val))' – 2011-05-03 23:14:40

+0

应该是可以的。尝试一下! – 2011-05-03 23:18:28