2016-12-16 7 views
4

私はより多くの異なる型を作成するためにコード内にnewtypeラッパーを使用しています。私はまた、リード/ショーを使って、特に強く型付けされた設定ファイルのシンプルな形式として、たくさんの安価なシリアル化を行います。今日はこのに走った:データ構造を含む派生クラスのインスタンスが使用されていません

例は次のように開始し、私は、アンラップのためにという名前のフィールドと一緒に、のIntをラップアラウンドする簡単なのnewtypeを定義しますから、これらのいずれかを読み取ることが

module Main where 

import Debug.Trace (trace) 
import Text.Read (readEither) 


newtype Bar = Bar { unBar :: Int } 
    deriving Show 

カスタムインスタンス単純なInt構文です。ここでのアイデアは、 "Bar {unBar = 42}"の代わりに "42"を設定ファイルに入れることができれば嬉しいです。

このインスタンスにはトレースがあり、このインスタンスがいつ表示されるかがわかります問題を観察するときに実際に使用されます。

instance Read Bar where 
    readsPrec _ s = [(Bar i, "")] 
     where i = read (trace ("[debug \"" ++ s ++ "\"]") s) 

バーを含む別のタイプ。これは自動的に読み取りを派生させます。

data Foo = Foo { bar :: Bar } 
    deriving (Read, Show) 


main :: IO() 
main = do 

だけでバータイプをデシリアライズすると、正常に動作して

print $ ((readEither "42") :: Either String Bar) 
    putStrLn "" 

上記読むインスタンスを使用しますが、バーを含むいくつかの理由はFooのために、そして自動的に読む派生、ドリルダウンしてピックアップされていませんバーのインスタンス!

print $ ((readEither "Foo { bar = 42 }") :: Either String Foo) 
    putStrLn "" 

のでOK(トレースからのデバッグメッセージがいずれかの表示されていないことに注意してください)、どのようにバーのデフォルトの表示形式については、デフォルトは右読むと一致する必要がありますか?

print $ ((readEither "Foo { bar = Bar { unBar = 42 } }") :: Either String Foo) 

いいえ!どちらもうまくいかない!ここでも、デバッグメッセージはありません。ここで

は実行出力です:

$ stack exec readbug 
    [debug "42"] 
    Right (Bar {unBar = 42}) 

    Left "Prelude.read: no parse" 

    Left "Prelude.read: no parse" 

これは私にはバグだらけに見えるが、私はそれが間違ってやっていることを聞いてみたいです。

上記のコードの完全な使用例があります。ファイルsrc/Main.lhstest project on darcshub

+3

これは非常に良い質問です。誰かがあなたのコードのデバッグを開始したことをいかに簡単にしたのが大好きです。うまくいけば私の答えはあなたが持っている特定の問題を特定するのに役立ちます。それはさておき、私はデバッグ以外にも 'Read'を使うことを勧めないでしょう。そして、' read 'を確認してください。 show = id'。私は設定をJSON(エンコード/デコードに 'aeson'を使います)に入れたり、(カスタムパーサーを主張するなら)' attoparsec'や 'megaparsec'のようなものを使います。 'Read'は非効率なパーサです。なぜならどこにでも戻ってくるからです。 – Alec

+2

あなたは間違っています: 'Foo' *の派生インスタンスはあなたが書いた' Bar'に対して 'Read'インスタンスを使用しています! 'Bar'が残りの入力をすべて消費したと誤って報告しているので、' Bar'の値を強制するのは面倒です(そのため、 'trace'を使ってサンクを強制することはありません)。 'Foo'読者は成功するために'} 'を見ません。 –

+0

@Alec私はconfigsのためのJSONの使用を考えていませんでした。入力と階層構造を保持します。そして、あなたは他の言語/システムで使用可能な設定ファイルで終わります。私は今少し新しいタイプでこれを探検します。ありがとう! – dino

答えて

4

に表示してください。問題はReadです。 readsPrecは、Barの後にもう少し多くのものが表示される可能性を考慮する必要があります。 Quoting the Prelude(<parsed value>, <remaining string>)ペアのリストを返す、文字列の先頭から値を解析する

readsPrec d s試みます。解析が正常に行われなかった場合、返されるリストは空です。あなたのケースでは

、あなたが望む:

instance Read Bar where 
    readsPrec d s = [ (Bar i, s') | (i, s') <- readsPrec d tracedS ] 
     where tracedS = trace ("[debug \"" ++ s ++ "\"]") s 

すると、次のような作品:

ghci> print $ ((readEither "Foo { bar = 42 }") :: Either String Foo) 
[debug " 42 }"] 
Right (Foo {bar = Bar {unBar = 42}}) 

あなたの他の問題、すなわち:

ので、[OK]を、方法についてBarのデフォルトのShowフォームは、デフォルトのRead権限と一致する必要がありますか?

print $ ((readEither "Foo { bar = Bar { unBar = 42 } }") :: Either String Foo) 

あなたのせいです:あなたはread . showアイデンティティ操作ではないとBarなためReadインスタンスを定義しました。 FooReadを派生した場合、BarReadインスタンスを使用します(Readを派生させた場合、Barが生成したコードを再生成しようとしません)。

+1

私はそれが正当なものだと知っていますが、私はそれを示唆することはできません: 'Readバーはwhere readsPrec = coerce(readsPrec @Int)' –

+0

@Alec Ah!私は 'read'と' readsPrec'の違いを認識しませんでしたが、同じ関数を使う方がはるかに意味があります。ありがとうございました。 – dino

+0

@ダニエルワーグナーオハイオ州、私はそれの敏感さが好きです。私はData.Coerceについて知らなかった。もっと効率的かどうか疑問に思います。また、 '-XTypeApplications'が必要です。ありがとう! – dino

関連する問題