ライブラリで魔法をかけ、製品タイプを多形的に分解できるようにしたいと思います。これは、多かれ少なかれ機能するモックアップであり、私がやりたいことを示しています。
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, UndecidableInstances #-}
newtype Wrapped a = Wrapped { unwrap :: a }
-- our example structure
ex :: (Int, (Int, Int))
ex = (1,(2,3))
class WrapDecomp x y | y -> x where
decomp :: x -> y
instance (WrapDecomp x x', WrapDecomp y y')=> WrapDecomp (x,y) (x',y') where
decomp (x,y) = (decomp x, decomp y)
instance WrapDecomp x (Wrapped x) where
decomp = Wrapped
example = let w = decomp ex
(w0, w1) = decomp ex
(w0', (w1', w2')) = decomp ex :: (Wrapped Int, (Wrapped Int, Wrapped Int))
in print $ ( unwrap w, unwrap w0, unwrap $ snd w1, unwrap $ fst w1 )
-- Also works:
-- in print $ ( unwrap w, unwrap w0, unwrap w1 )
私の実際のアプリケーションはライブラリであり、上記で気付いたいぼを許容できるようにする2つのプロパティがあります。
型コンストラクターは
Wrapped
エクスポートされませんunwrap
ユーザーは常にバインディング内のすべてのデータを呼び出しますWrapped
(私のアプリケーションの詳細が退屈なため)。したがって、実際にはあいまいさがあってはなりません。
コンセンサスUndecidableInstances
はそれほど悪くはないようですが、先に進む前に、上記がコーシャであることを確認したいと思います。
ソリューション付きの更新
私はこれに少し戸惑いましたが、TypeFamilies
次のようにして問題を解決することができました:
{-# LANGUAGE TypeFamilies #-}
class Out a where
type In a :: *
decomp :: In a -> a
instance Out (Wrapped a) where
type In (Wrapped a) = a
decomp = Wrapped
instance (Out a, Out b)=> Out (a,b) where
type In (a,b) = (In a,In b)
decomp (x,y) = (decomp x, decomp y)