5

異種のインデックス構造を構築しようとしていて、次の解決策を思いつきましたが、存在する型を使用しないように言われました

より良い解決策が見えますか?

typeインターフェイスの定義 (とclass) と具体的な実装 (dataと)を分離したいと思いますinstance。@hammar のコメントに従って編集Showします。またmyData、追加のレコードでより複雑になります。

これがより良い解決策につながる場合、正確な要件は、マップのマップ (内部マップ) を構築することです。各内部マップは同種で、次の形式になっていますが、Map String a各内部マップはその値に異なる型を適用する場合があります。また、2 レベルのインデックス構造と考えることもできます。実装は a を使用するData.Map必要はありませんが、効率的でなければなりません。

{-# LANGUAGE ExistentialQuantification #-}
module Scratch.SO_ExtistentialTypes where

import Data.Map

data HeteroValue = forall a. Show a => HV a 

instance Show HeteroValue where
    show (HV b) = show b

type MyMap = Map String HeteroValue

class MyClass c where 
    getMyMap :: c -> MyMap

data MyData = MyData {
    myMap ::  MyMap
}

instance MyClass MyData where
    getMyMap = myMap

このスニペットは ghci を使用して実行できます

let myMap = fromList [("key1", HV "abc"), ("key2", HV 123)] :: MyMap
let myData = MyData myMap
getMyMap myData 
4

3 に答える 3

6

これはオブジェクト指向言語の良いパターンですが、よく知られているHaskellのアンチパターンです。その記事を読んでください。私が言う何よりもそれを読んでほしい。

this answerも参照してください。 ただし、GADT は実存型よりもエレガントだと思います (以下を参照)。


オブジェクト指向プログラミングを再実装するための最良の関数型プログラミングの方法ではなく、プログラムを作成するための最良の関数型プログラミングの方法を見つけるようにしてください。オブジェクト指向スタイルでプログラミングするという希望以外に、コードの目的をまだ明確にしていません。

そうすれば、彼はより優れた C プログラマーになるかもしれません。英語を話したり、CNNを見たり、マクドナルドを食べたりするためだけにフランスを訪れるのはやめましょう!できるだけ機能的な方法でコードを記述しようとする必要があるという意味があります。)


データが契約に従っていること以外の情報を本当に知りたくない場合、1 つの方法は GADT を使用することです。Haskell はあなたの主張を支持することに注意してください。思慮のない設計上の決定から抜け出すためのキャスティングはありません。(キャストは、コンパイル時チェックを実行時チェックに変える方法、または別の言い方をすれば、コンパイル時エラーを実行時エラーに変える方法です。私はそれが良いことだとは思いません。)

{-# LANGUAGE GADTs #-}

class Contract a where
   toString :: a -> String
   fromInts :: Int -> Int -> a
   alter :: a -> a
   -- other functionality

data Encapsulated where
   Encapsulate :: Contract a => a -> Encapsulated

データをカプセル化したら、通常のデータ型であるかのように好きなことを行うことができ、次のようにContracted 機能を回復できます。

munged :: Encapsulated -> String
munged (Encapsulate a) = toString.alter.alter.alter $ a

必要に応じて、大量のEncapsulatedデータをマップに格納できData.Mapます。実存のために特別なことをしたり、再実装したりする必要はありません。なぜなら、強力な機能パラダイムがあるからです。データに関する仮定Data.Mapまったくありません。これはパラメトリックポリモーフィズムであり、何に対しても機能します。何でも、機能さえも。唯一の仮定は、キーがソート可能 ( Ord k =>) であり、データが同種であることです (そして、異種データから作成されているにもかかわらず、GADT は同種です)。


それはあなたが求めたことを実行する 1 つの方法ですが、あなたが何を求めているかを知っていれば、より適切なアドバイスを提供できます。(おそらく別の新しい質問です!)私がリンクした記事を本当に読んで、クラスを関数/結果でいっぱいのデータ型として実装し、インスタンスをそのデータ型の関数として実装できるかどうかを確認してください。

于 2012-10-28T09:28:12.587 に答える
4

「異種コレクション」を行う 1 つの方法は、Data.Dynamicを使用することです。

module Scratch.SO_Dyn where

import Data.Dynamic
import Data.Map

type MyMap = Map String Dynamic

class MyClass c where 
    getMyMap :: c -> MyMap

data MyData = MyData {
    myMap ::  MyMap
}

instance MyClass MyData where
    getMyMap = myMap

このマップに入れたいデータは Typeable から派生している必要があります。と
を使用します。 http://www.haskell.org/ghc/docs/7.6.1/html/users_guide/deriving.html#deriving-typeableも参照してください。{-# LANGUAGE DeriveDataTypeable #-}deriving (Data, Typeable)

次に、 を使用してデータをDynamic型にキャストし、 を使用して型toDynから安全にキャストできます。DynamicfromDynamic


これは完全に有効なアプローチですが、私や他の多くの Haskeller は、真に異種のコレクションに頼るのではなく、カスタム データ型を作成することを検討することを強くお勧めします。(ハロウィーンの精神で) このマップに入れるものはCats、Witches、およびGhouls だけであるという事実を知っているとします。

data Cat = ...
data Witch = ...
data Ghoul = ...

考えられる各オプションにタグを付けるだけで、後でそれぞれが何であるかを判断できます。

data HeteroValue
  = DarkOmen Cat
  | Hag Witch
  | Haunting Ghoul

case (Map.lookup "Midnight visitor" theMap) of
  Just (DarkOmen cat) -> hiss cat
  Just (Hag witch) -> cackle witch
  Just (Haunting ghoul) -> spook ghoul
  Nothing -> error ...
于 2012-10-27T20:44:21.927 に答える
0

a でできる唯一のことは、 aHeteroValueに変換することです。showつまり、 に変換しStringます。値を保存しても意味がないことを念頭に置いて、変換された文字列を保存することもできます。

type MyMap = Map String String

また

data HeteroData = HD { getShow :: String }
type MyMap = Map String HeteroData

これは、他の型クラスに簡単に採用できます。

代わりに でパターン マッチングなどを行う場合はHeteroValue、存在型が適切なソリューションです。

于 2012-10-28T01:50:28.430 に答える