0

'hidden'(推定)型と具体的な値を持つデータ型があります。今、私はこれらの両方を変更する関数を実装しようとしましたが、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))
4

1 に答える 1

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に取得し、それを破棄し、タグと値を値の型によって決定される新しいタグに置き換えるかに注意してください。

于 2011-05-03T21:39:20.967 に答える