2016-12-13 44 views
3

再次感謝您的幫助!如何縮放monad變壓器?

我正在廣泛使用E. Kmett的鏡頭庫,以避免X/Y問題我將解釋一些上下文。

我工作的一個可擴展的文本編輯器,並希望提供擴展作者用單子DSL,一個Alteration是一個StateT在Store類型,它基本上存儲整個文本編輯器單子轉換堆棧。在Store內部是Editor其具有Buffers。用戶可以指定Alteration來對整個商店進行操作,但爲了簡化操作,我還提供了BufAction,它僅通過一個緩衝區進行操作。

我打算用一種叫做bufDo助手運行在BufAction在每個Buffer實施這一點,並運行於「專注」 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暗示一些東西,我結束了這一點,這是遠離優雅:

bufDo :: (Applicative (Zoomed BufAction()), Zoom BufAction Alteration Buffer Store) => BufAction() -> Alteration() 

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

這使得GHC快樂對於那些定義,但是當我嘗試實際使用其中任何一個,我得到這些錯誤:

- 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 -newtype)。 – freestyle

+0

我看到了一個,但仍然有點困惑,他們給出了一個解決方案,但不能很清楚地解釋它。 –

回答

2

似乎有一個Zoomed類型系列,用於指定我們縮放時我們將具有何種「效果」。在某些情況下,對於一個單子變壓器Zoomed類型實例出現捎帶上Zoomed爲底層單子,例如

type Zoomed (ReaderT * e m) = Zoomed m 

鑑於AlterationBufAction只是newtypes通過國家變壓器,或許我們可以這樣做相同:

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

type instance Zoomed BufAction = Zoomed (StateT Buffer IO) 

然後我們必須提供Zoom實例。Zoom是一個多參數的類型類和四個參數似乎原始單子縮小單子原始狀態縮小狀態

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

我們只是解開BufAction,具有放大下面的monad,並且換成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另外,爲什麼我們需要不可判定的實例?最後,爲什麼我需要一個類型家族「縮放」和縮放實例?我很想理解爲什麼這一切都有效,以及爲什麼這些都是必需的。乾杯! :) –

+1

@Chris Penner類型系列有點像類型級別的函數。它們採用一種類型(在我們的例子中爲'BufAction')併產生另一種類型(在我們的例子中爲'Zoomed(StateT Buffer IO)')。與常規功能相比,它們具有一些特殊性:語法不盡相同,不同的「案例」可以分散在不同的模塊中,等等。你可以使用':kind!'命令在'ghci'中運行一個類型族,比如':kind!縮放(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

是的,如果您想增加使用'zoom'或類似'zoomBufActionToAlteration'的東西的可能性。如果你有很多不同的可縮放的東西,這個原因可能(正如@danidiaz所寫的那樣)。 – freestyle