このインスタンスを自動的に導出できないという事実は、100% 正しい動作です。期待どおりに動作しない理由は、インスタンスFromJSON (Map Text v)
が type の値で使用できることを知る方法がないためMap MyText v
です。これは、 a の作成と操作がそのキーMap
のインスタンスに基づいており、Ord
(コンパイラにとって) すべての xy(x == y) == (MyText x == MyText y)
についてそれを知る方法がないMap Text v
ためMap MyText v
です。より技術的には、のロール宣言は次のMap
とおりです。
type role Map nominal representational
基本的に、これはMap k v
、最初の型パラメーターが同一である他のマップに対してのみ強制可能であることを示しています。ウィキには次のように書かれています。
インスタンス Coercible ab => Coercible (T a) (T b) があるのは、最初のパラメーターが表現の役割を持っている場合のみです。
このクラスCoercible
は、GHC の最近のバージョン (7.8?) で型安全な強制を行うために使用されます。の
のインスタンスを派生させる予定がある場合は、インスタンスが同じであるため、に強制してOrd MyText
も安全です。これには を使用する必要があります。ただし、インスタンスを自分で作成する必要があります。Map Text v
Map MyText v
Ord
unsafeCoerce
instance ToJSON v => ToJSON (Map MyText v) where
toJSON = toJSON . (unsafeCoerce :: Map MyText v -> Map Text v)
instance FromJSON v => FromJSON (Map MyText v) where
parseJSON = (unsafeCoerce :: Parser (Map Text v) -> Parser (Map MyText v)) . parseJSON
独自のインスタンスを作成する予定がOrd
ある場合、上記は絶対に安全ではありません。あなたの解決策は正しいですが、あまり効率的ではありません。以下を使用します。
toJSON = toJSON . M.mapKeys (coerce :: MyText -> Text)
parseJSON = fmap (M.mapKeys (coerce :: Text -> MyText)) . parseJSON
mapKeysMonotonic
Ord インスタンスによっては、より効率的な代わりに使用できる場合があります。Data.Map
をいつ使用できるかについては、 のドキュメントを参照してくださいmapKeysMonotonic
。
次に、明らかなことが機能します。
data Bar = Bar deriving (Eq, Ord, Generic)
instance ToJSON Bar
instance FromJSON Bar
data Foo = Foo (Map MyText Bar) deriving (Generic)
instance ToJSON Foo
instance FromJSON Foo
-- Using GeneralizedNewtypeDeriving
newtype Foo2 = Foo2 (Map MyText Bar) deriving (FromJSON, ToJSON)
完全なコード:
{-# LANGUAGE DeriveGeneric, GeneralizedNewtypeDeriving, FlexibleInstances #-}
module Test where
import Data.Aeson
import GHC.Generics (Generic)
import qualified Data.Map as M
import Data.Map (Map)
import Data.Text (Text)
import GHC.Prim (coerce)
import Unsafe.Coerce (unsafeCoerce)
import Data.Aeson.Types (Parser)
newtype MyText = MyText {unMyText::Text} deriving (Eq, Ord, Generic, ToJSON, FromJSON)
instance ToJSON v => ToJSON (Map MyText v) where
-- toJSON = toJSON . M.mapKeys (coerce :: MyText -> Text)
toJSON = toJSON . (unsafeCoerce :: Map MyText v -> Map Text v)
instance FromJSON v => FromJSON (Map MyText v) where
-- parseJSON x = fmap (M.mapKeys (coerce :: Text -> MyText)) (parseJSON x)
parseJSON x = (unsafeCoerce :: Parser (Map Text v) -> Parser (Map MyText v)) (parseJSON x)
data Bar = Bar deriving (Eq, Ord, Generic)
instance ToJSON Bar
instance FromJSON Bar
data Foo = Foo (Map MyText Bar) deriving (Generic)
instance ToJSON Foo
instance FromJSON Foo
newtype Foo2 = Foo2 (Map MyText Bar) deriving (FromJSON, ToJSON)