0

私はハスケルタイプのクラスで遊んでいましたが、私は誰かが私を解決するのを助けてくれることを願っています。私は、素早い背景から来て、プロトコル指向の知識の一部をHaskellコードに移植しようとする "試み"を考えてください。タイプクラッセインスタンスのリスト

は当初、私はちょうど別の実装、同じ構造を持っていたJSONパーサーの束を宣言:

data Candle = Candle { 
    mts :: Integer, 
    open :: Double, 
    close :: Double 
} 

data Bar = Bar { 
    mts :: Integer, 
    min :: Double, 
    max :: Double 
} 

その後、私は彼らの基本的な操作の定義する「クラス」を作成することにしました:

class GenericData a where 
    dataName :: a -> String 
    dataIdentifier :: a -> Double 
    dataParsing :: a -> String -> Maybe a 
    dataEmptyInstance :: a 


instance GenericData Candle where 
    dataName _ = "Candle" 
    dataIdentifier = fromInteger . mts 
    dataParsing _ = candleParsing 
    dataEmptyInstance = emptyCandle 

instance GenericData Bar where 
    dataName _ = "Bar" 
    dataIdentifier = fromInteger . mts 
    dataParsing _ = barParsing 
    dataEmptyInstance = emptyBar 

私の最初のコードの匂いは、必要でないときに "a"を含める必要がありましたが(dataNameまたはdataParsing)、その後は手続きしました。今

analyzeArguments :: GenericData a => [] -> [String] -> Maybe (a, [String]) 
analyzeArguments [] _    = Nothing 
analyzeArguments _ []    = Nothing 
analyzeArguments name data 
    | name == "Candles" = Just (head possibleCandidates, data) 
    | name == "Bar" = Just (last possibleRecordCandidates, data) 
    | otherwise = Nothing 

possibleCandidates :: GenericData a => [a] 
possibleCandidates = [emptyCandle, emptyBar] 

私はどちらかのインスタンスが構文解析を実行するために選択すべきかを選択するときに、私はいつも

• Couldn't match expected type ‘a’ with actual type ‘Candle’ 
    ‘a’ is a rigid type variable bound by 
    the type signature for: 
     possibleCandidates :: forall a. GenericData a => [a] 
    at src/GenericRecords.hs:42:29 

次のエラーを取得する私の目的は、他のためGenericDataのインスタンスのリストを作成することでした関数は、正しいdataParserを実行するために選択されている関数に依存します。私はこれが型クラスチェッカーである* -> Constraintと関係がありますが、この矛盾を解決する方法はまだ見つけていないことを理解しています。私はいくつかのGHC言語拡張を使用していますが、問題を解決したものはありません。

+2

ここのコードの匂いはおそらくあなたのタイプのクラスだと思います。メソッドのすべてが 'a'を最初の引数として取る型クラスを見ると、誰かがオブジェクト指向クラスをHaskell型クラスに振り回そうとしているのか疑いがあります。同じADTの下に 'Bar'コンストラクタと' Candle'コンストラクタを配置したいと思うように思えます。 – Alec

+3

これは、既知の[反パターン](https://lukepalmer.wordpress.com/2010/01/24/haskell-antipattern-existential-typeclass/)に向かって急速に下降すると思います。 – chi

+2

何が間違っているかについての2つのコメント(解決策の提案なし)。 1.リストは同種(すべての要素が同じ型)なので、 '[emptyCandle、emptyBar]'が正しく出力されます。 2.型 'Foo a => [a]'は、この値のユーザに、 'Foo'のどのインスタンスが選択されているかを制御します。実装者は 'Foo'のインスタンスである* any *型の値のリストを生成する準備をしなければなりませんが、この型の実装者がインスタンスである*好みの*型を選ぶと思っているようですFooの –

答えて

2

あなたは型シグネチャを持っている:

possibleCandidates :: GenericData a => [a] 

事はあなたがいる限り、それはGenericDataであるように、そのリストに何かを置くことができることを意味するあなたは可能性があります。しかし、それはHaskellの型システムが実際に動作する方法ではありません。値possibleCandidatesは、GenericDataクラスを持つすべてのタイプのリストですが、リストの各エレメントはと同じタイプでなければなりません。

GHCエラーメッセージは、リストの最初の要素がCandleであるため、リストの残りの部分もCandleであると考えていますが、2番目の要素は実際にはBarです。

今では、Haskellで異種のリスト(および他のコレクション)を作成する方法がありますが、ほとんど正しいことはありません。この問題への

一つの典型的な解決策はただ1 sum data typeにダウンすべてをマージすることです:

data GenericData = GenericCandle Candle | GenericBar Bar 

あなたも、間接のステップを断念し、単なるデータ構造に直接キャンドル、バーのデータを入れることができます。あなただけのデータ型を持っているし、自分のクラスの機能が正常な機能になっ

今の代わりにFAクラス:

dataName :: GenericData -> String 
dataIdentifier :: GenericData -> Double 
dataParsing :: GenericData -> String -> Maybe a 
dataEmptyInstance :: String -> GenericData 

あり、この作業をするためにいくつかの他のより複雑な方法がありますが、合計のデータ型は法案を収まる場合これを使って。 Haskellのパーサーは、結果として大量のデータ型(通常は再帰的なデータ型)を持つことが非常に一般的です。例のためのタイプの標準JSONライブラリAesonを見てください。

+1

私はこのブログ記事に基づいてあなたのソリューションに似た何かをやりました: http://www.haskellforall.com/2012/05/scrap-your-type-classes.html – Invoke

関連する問題