2016-12-23 24 views
2

JSONオブジェクトを取得し、その中のすべての文字列値を変更して新しいJSONオブジェクトを返す関数を作成しようとしています。これまでのところ、私のコードは次のとおりです。ハスケルのJSONデータ構造を再帰的に変更します。

applyContext :: FromJSON a => a -> a 
applyContext x = 
    case x of 
    Array _ -> map applyContext x 
    Object _ -> map applyContext x 
    String _ -> parseValue x 
    _  -> x 

しかし、コンパイラは、第二caseライン文句:

Couldn't match expected type `[b0]' with actual type `a' 
    `a' is a rigid type variable bound by 
    the type signature for: 
     applyContext :: forall a. FromJSON a => a -> a 
    at app\Main.hs:43:17 

私はmapは、リスト上で動作するように意図されているため、つまり推測しているが、私は持っているでしょう代わりにData.HashMap.Lazy.mapを使用することを期待していました。これは、そのタイプが実際にその場合のものであるからです。私は明示的にその機能を使用する場合、私はそれが他のケースのために動作しないので、私はその程度のaを制約していないことからも、理にかなって

Couldn't match expected type `HashMap.HashMap k0 v20' with actual type `a' 

を取得します。私はこれで十分な明示的な型をスローすれば、動作させることができると思うが、もっとシンプルにすべきだと感じている。この関数を書く慣用的な方法は何ですか、あるいはこれが良い場合は、型を正しく取得する最も単純な方法は何でしょうか?

+0

あなたがいない 'Data.HashMap.Lazy.map'、map''呼び出します。 Haskellは間違った型の引数を指定したからといって二度目に推測しようとしません。 – chepner

+4

慣用的アプローチはタイプから始まり、「これは理にかなっていますか?私が知る限り、それはしません。最小限のようで、 'a'に' ToJSON'制約も必要です。まずは、「Value - > Value」型の関数を記述し、次に任意の型と「Value」の間で変換を行う第2の関数を記述します。そうすることで、どちらの制約が必要なのかが明確になります。 – Carl

+0

これは価値がありますが、これは 'lens'と' lens-aeson'が非常にきれいに解決するような問題ですが、なぜそれをしばらく避けたいのか分かります。 – Carl

答えて

1

まず、FromJSON a => aはどういう意味ですか?それは、どんなタイプのものでもかまいませんが、クラスFromJSONからのものに過ぎないということです。このクラスには、非常に異なる構造を持つタイプを含めることができ、パターンマッチングを行うことはできません。あなたはプログラマがクラスFromJSONの宣言で指定されたものだけを行うことができます。基本的には、1つの方法parseJSON :: FromJSON a => Value -> Parser aがあります。

第2に、JSONの同形表現を使用して作業する必要があります。タイプValueは良いものです。したがって、Value -> Valueのような関数で主な作業を行うことができます。その後、一般的な種類の場合はparseJSONtoJSONでこの機能を構成できます。

このよう

:あなたはlensValue -> Valuelens-aesonのようなより複雑な変換を書くことができます

change :: Value -> Value 
change (Array x) = Array . fmap change $ x 
change (Object x) = Object . fmap change $ x 
change (String x) = Object . parseValue $ x 
change x   = x 

apply :: (ToJSON a, FromJSON b) => (Value -> Value) -> a -> Result b 
apply change = fromJSON . change . toJSON 

unsafeApply :: (ToJSON a, FromJSON b) => (Value -> Value) -> a -> b 
unsafeApply change x = case apply change x of 
         Success x -> x 
         Error msg -> error $ "unsafeApply: " ++ msg 

applyContext :: (ToJSON a, FromJSON b) => a -> b 
applyContext = unsafeApply change 

。たとえば:

import Control.Lens 
import Control.Monad.State 
import Data.Aeson 
import Data.Aeson.Lens 
import Data.Text.Lens 
import Data.Char 

change :: Value -> Value 
change = execState go 
    where 
    go = do 
     zoom values go 
     zoom members go 
     _String . _Text . each %= toUpper 
     _Bool %= not 
     _Number *= 10 

main = print $ json & _Value %~ change 
    where json = "{\"a\":[1,\"foo\",false],\"b\":\"bar\",\"c\":{\"d\":5}}" 

出力は次のようになります。

"{\"a\":[10,\"FOO\",true],\"b\":\"BAR\",\"c\":{\"d\":50}}" 
関連する問題