2016-12-13 8 views
3

もう一度お世話になりました!モナドトランスをズームするには?

X/Yの問題を避けるために、私はKmett's Lensライブラリを大量に使用しています。少し説明します。

私は拡張可能なテキストエディタに取り組んでおり、モナドDSLを拡張ライターに提供したいと考えています。Alterationは基本的にテキストエディタ全体を格納するStoreタイプのStateTを持つモナドトランススタックです。 Storeの内部にはがあり、これはBuffer秒です。ユーザーはAlterationを指定して店全体を操作することができますが、事実を単純化するために、ただ1つのバッファーで動作するBufActionも提供しています。

私は各 Bufferオーバー BufActionを実行 bufDoと呼ばれるヘルパーを使用してこれを実装する上で計画していた

、およびBuffer「集中」に関するBufActionを実行しますfocusDo。ここではいくつかの状況です:ここでは

data Store = Store 
    { _event :: [Event] 
    , _editor :: E.Editor 
    , _extState :: Map TypeRep Ext 
    } deriving (Show) 

data Editor = Editor { 
    _buffers :: [Buffer] 
    , _focused :: Int 
    , _exiting :: Bool 
} deriving Show 

data Buffer = Buffer 
    { _text :: T.Text 
    , _bufExts :: Map TypeRep Ext 
    , _attrs :: [IAttr] 
    } 

newtype Alteration a = Alteration 
    { runAlt :: StateT Store IO a 
    } deriving (Functor, Applicative, Monad, MonadState Store, MonadIO) 

newtype BufAction a = BufAction 
    { runBufAction::StateT Buffer IO a 
    } deriving (Functor, Applicative, Monad, MonadState Buffer, MonadIO) 

bufDofocusDoのための私の提案の実装:

bufDo :: ??? 
bufDo = zoom (buffers.traverse) 

-- focusedBuf is a Lens' over the focused buffer (I just 'force' the traversal using ^?! in case you're wondering) 
focusDo :: ??? 
focusDo = zoom focusedBuf 

私はタイプを追加しようとすると、これは私の頭の中で理にかなっていると型チェックをするために近づくが、それらを私は少し混乱し、GHCはいくつかのことを示唆していると私ははるかにエレガントからである、これで終わった:

GHCを幸せにする
bufDo :: (Applicative (Zoomed BufAction()), Zoom BufAction Alteration Buffer Store) => BufAction() -> Alteration() 

focusDo :: (Functor (Zoomed BufAction()), Zoom BufAction Alteration Buffer Store) => BufAction() -> Alteration() 

私はズームのインスタンスを指定する必要がありますようにそれはそう周り

- No instance for (Functor (Zoomed BufAction())) 
    arising from a use of ‘focusDo’ 

    - No instance for (Applicative (Zoomed BufAction())) 
    arising from a use of ‘bufDo’ 

見ると、私はどのように非常によく分からない。私は実際にそれらのいずれかを使用しようとすると、それらの定義のために、私はこれらのエラーを取得しますそれを行う。

誰でもアイデアがありますか?なぜ私はZoomインスタンスが必要なのかを説明することができればそれも大好きです(そうであれば)。

乾杯!

+0

これは、本質的に[この質問](http://stackoverflow.com/questions/29407289/lens-zoomingの複製であります-新しいタイプ)。 – freestyle

+0

私はそれを見ましたが、少し混乱していましたが、彼らは解決策を提供しましたが、それを非常にはっきりと説明しません。 –

答えて

2

Zoomedファミリーがあり、ズームするときにどのような「効果」があるかを指定するために使用されているようです。いくつかのケースでは、モナド変換のためのZoomed型インスタンスはAlterationBufActionだけで、おそらく我々が行うことができ、状態の変圧器を介してnewtypesされていることを考えると例

type Zoomed (ReaderT * e m) = Zoomed m 

のために、基礎となるモナドのためZoomedに便乗するように見えます同じ:

{-# language TypeFamilies #-} 
{-# language UndecidableInstances #-} 
{-# language MultiParamTypeClasses #-}  

type instance Zoomed BufAction = Zoomed (StateT Buffer IO) 

次に、Zoomインスタンスを指定する必要があります。でズーム、私達はちょうどBufActionをアンラップ

instance Zoom BufAction Alteration Buffer Store where 
    zoom f (BufAction a) = Alteration (zoom f a) 

:は、ズームアウト状態Zoomは、マルチパラメータ型クラスであり、4つのパラメータが元モナドように見える、はモナドをズームアウトし、元の状態基底のモナドをラップしてAlterationとします。

この基本的なテストtypechecks:

foo :: Alteration() 
foo = zoom (editor.buffers.traversed) (return() :: BufAction()) 

私はあなたがZoomインスタンスを定義避け、特別目的zoomBufActionToAlteration機能に

zoomBufActionToAlteration :: LensLike' (Zoomed (StateT Buffer IO) a) Store Buffer 
          -> BufAction a 
          -> Alteration a 
zoomBufActionToAlteration f (BufAction a) = Alteration (zoom f a)  

を持っている可能性が信じている。しかし、あなたは多くを持っている場合さまざまなズーム可能なものの中から、それぞれのズームファンクションの名前を覚えておくことができます。これは、typeclassが助けることができる場所です。

+0

私はこれを試してみよう!ありがとう! 'type instance'の行をもう少し詳しく説明できますか?私はタイプファミリーをまだ学んでいません:Pまた、なぜわれわれは決定不能なインスタンスを必要としますか?最後に、なぜ型のファミリー「Zoomed」とZoomインスタンスの両方が必要なのですか?私はなぜそれがすべての作品と、なぜそれはすべての必要性を理解したいと思います。乾杯! :) –

+1

@Chris Pennerタイプファミリーは、タイプレベルの機能に似ています。彼らは型(私たちの場合は 'BufAction')をとり、別の型(この場合は' Zoomed(StateT Buffer IO) ')を生成します。彼らは通常の関数と比べていくつかの特殊性を持っています。構文は同じではなく、異なる "ケース"を異なるモジュールに分散することができます。 ':kind!'のような ':kind!'コマンドを使って 'ghci'の型族を"実行 "することができます。ズーム(StateTバッファIO) ' – danidiaz

+1

@Chris Penner 'UndecidableInstances'は、複雑なインスタンス定義を扱うときに、型チェックが終了できるかどうかをGHCが確かめることができるので必要です。 'UndecidableInstances'を有効にすると、コンパイラは、終端のない型チェックを心配しないでください。時間がかかりすぎるとCtrl + Cだけで完了します。タイプチェックが実際に終了すると、それは有害な拡張ではありません。 – danidiaz

3

@danidiazの回答に追加して。


基本的に、あなたがこの方法でZoomインスタンスを避けることができます。

bufDo :: BufAction() -> Alteration() 
bufDo = Alteration . zoom (editor . buffers . traverse) . runBufAction 
+0

これは素晴らしい作品です! @ danidiazの答えと一緒に説明していただきありがとうございます。最終的にタイプファミリーについて学ぶのはいいことです!しかし、このソリューションの代わりにタイプファミリの魔法を使う必要がある理由は何ですか? –

+0

「zoomBufActionToAlteration」のようなものを使用する可能性を追加したい場合はあります。あなたがいろいろなズーム可能なものをたくさん持っているなら、この理由は(おそらく@danidiazを書いたように)。 – freestyle

関連する問題