0

私は何でも含むことができる普遍的なデータ型を定義しました (まあ、現在の実装では完全に何もではありません)!

ここにあります(完全なコード):

{-#LANGUAGE NoMonomorphismRestriction#-}
{-#LANGUAGE GADTs#-}
{-#LANGUAGE StandaloneDeriving#-}

data AnyT where
    Any :: (Show a, Read a) => a -> AnyT

readAnyT :: (Read a, Show a) => (String -> a) -> String -> AnyT
readAnyT readFun str = Any $ readFun str

showAnyT :: AnyT -> String
showAnyT (Any thing) = show thing

deriving instance Show AnyT --Just for convinience!

a = [Any "Hahaha", Any 123]

そして、コンソールで遊ぶことができます:

*Main> a
[Any "Hahaha",Any 123]
it :: [AnyT]
*Main> readAnyT (read::String->Float) "134"
Any 134.0
it :: AnyT
*Main> showAnyT $ Any 125
"125"
it :: String

うーん、持ってるけど何とか加工しないと。たとえば、変換関数を定義しましょう (関数定義、前のコードに追加)。

toAnyT :: (Show a, Read a) => a -> AnyT -- Rather useless
toAnyT a = Any a

fromAny :: AnyT -> a
fromAny (Any thing) = thing

そして、問題があります!前のコードのfromAny定義が正しくありません! そして、私はそれを正しくする方法がわかりません。GHCiでエラーが発生します:

2.hs:18:23:
    Could not deduce (a ~ a1)
    from the context (Show a1, Read a1)
      bound by a pattern with constructor
                 Any :: forall a. (Show a, Read a) => a -> AnyT,
               in an equation for `fromAny'
      at 2.hs:18:10-18
      `a' is a rigid type variable bound by
          the type signature for fromAny :: AnyT -> a at 2.hs:17:12
      `a1' is a rigid type variable bound by
           a pattern with constructor
             Any :: forall a. (Show a, Read a) => a -> AnyT,
           in an equation for `fromAny'
           at 2.hs:18:10
    In the expression: thing
    In an equation for `fromAny': fromAny (Any thing) = thing
Failed, modules loaded: none.

エラーが発生する他の方法も試しました。


私はこれに対してかなり悪い解決策を持っています: showAnyTreadを介して必要な関数を定義します(以前の関数定義を置き換えます):

toAnyT :: (Show a, Read a) => a -> AnyT -- Rather useless
toAnyT a = Any a

fromAny :: Read a => AnyT -> a
fromAny thing = read (showAnyT thing)

はい、仕事です。私はそれで遊ぶことができます:

*Main> fromAny $ Any 1352 ::Float
1352.0
it :: Float
*Main> fromAny $ Any 1352 ::Int
1352
it :: Int
*Main> fromAny $ Any "Haha" ::String
"Haha"
it :: String

しかし、文字列を使用して変換するため、悪いと思います。

きちんとした良い解決策を見つけるのを手伝ってくれませんか?

4

3 に答える 3

1

GADT を使用して、存在するデータ型を作成しています。コンストラクターの型aは存在しましたが、それを回復する方法はありません。あなたが利用できる唯一の情報はShowReadインスタンスがあるということです。正確な型は忘れられています。これは、コンストラクターの型が型システムに指示するものであるためです。「このタイプに適切なインスタンスがあることを確認してから、それが何であるかを忘れてください。」

ところで、見逃した機能が 1 つあります。

readLike :: String -> AnyT -> AnyT
readLike s (Any a) = Any $ read s `asTypeOf` a

パターン マッチのコンテキスト内で、コンパイラは、型が何であれインスタンスaが存在することを認識しRead、そのインスタンスを適用できます。といっても、どのタイプかは不明aです。しかし、それでできることは、それを表示するか、文字列を同じタイプとして読み取ることだけです。

于 2013-11-20T08:08:12.843 に答える
1

あなたが持っているのはExistential typeと呼ばれるものです。そのリンクをたどると、このパターンでコンテナー型内の「データ」を操作する唯一の方法は、型クラスを使用することであることがわかります。

現在の例では、インスタンスがa必要であると述べました。つまり、これらの型クラスの関数のみを使用でき、他には何も使用できません。さらに操作をサポートしたい場合は、必要な型クラスで制約する必要があります。ReadShowaa

次のように考えてください。ボックスには何でも入れることができます。その箱から何かを抽出するとき、その中に何でも入れることができるので、そこから何を得るかを指定する方法はありません。この箱の中に何でも食べられるものを入れることができると言うなら、この箱から何かを選ぶと、それは食べられると確信しています.

于 2013-11-20T08:06:31.100 に答える
1

最初の免責事項: あなたが解決しようとしている問題の全体像はわかりませんが、私が最初に抱いた印象は、この種の存在論の使用は仕事には不適切なツールであり、あなたはいくつかの実装を試みている可能性があるということです。オブジェクト指向言語では一般的ですが、Haskell にはあまり適していないコード パターンです。

とはいえ、ここにあるような存在型は、通常、何かを入れると型情報が永久に失われ、値を元の型に戻すことができないブラック ホールのようなものです。ただし、型クラスを介して存在値を操作できるため ( and で行ったShowように)、型クラスを使用して元の型情報を保持Readできます。Typeable

import Data.Typeable

data AnyT where
    Any :: (Show a, Read a, Typeable a) => a -> AnyT

これで、新しい制約も追加する限り、すべての関数を実装できます。

readAnyT :: (Read a, Show a, Typeable a) => (String -> a) -> String -> AnyT
readAnyT readFun str = Any $ readFun str

showAnyT :: AnyT -> String
showAnyT (Any thing) = show thing

toAnyT :: (Show a, Read a, Typeable a) => a -> AnyT -- Rather useless
toAnyT a = Any a

fromAnyを返すように実装できますMaybe a(取得する値が期待する型であるかどうか確信が持てないため)。

fromAny :: Typeable a => AnyT -> Maybe a
fromAny (Any thing) = cast thing
于 2013-11-20T08:20:13.637 に答える