2015-09-14 7 views
8

Я пытаюсь принять, например. ExceptT a (StateT A M), для некоторого конкретного типа A и монады M, и заверните их в мои новые монады.Уборка Monads - превращение монадного трансформатора в монумент newtype

Сначала я определил, что StateT A M часто появляется и в других контекстах, и, таким образом, я решил, что было бы лучше, чтобы обернуть, что только в монады M1 и затем обернуть ExceptT a M1 в M2.

Желаемое свойство сделать M1 и M2 экземпляры MonadState и класс M (позволяет предположить, что это называется MyMonadClass). Также M2 должен быть экземпляром MonadError.

Сначала я простые синонимы типа:

type MyState = StateT A M 
type MyBranch a = ExceptT a MyState 

тогда я думал, что я первый набросок декларации экземпляра (без реализации экземпляра) и то, где я первый застрявших. instance MonadState A (MyState), казалось, не был правильным синтаксисом. Я думал, что мне нужно будет создать newtype MyState' a = StateT a M, а затем type MyState = MyState A (Не рекомендуется использовать языковые расширения там, где это необходимо).

Однако, как только я начал преобразовывать синонимы в объявления newtype, я начал терять соединение с типами StateT A M и ExceptT ....

newtype MyState' s a = MyState' { runMyState :: s -> (s, a) } 
type MyState = MyState' A 
newtype MyBranch e a = MyBranch { runMyBranch :: MyState (Either e a) } 

Теперь трансформаторы, которые уже реализованы исчезнувшего, и я думаю, что я пытаюсь сделать что-то, что не имеет особого смысла. Поэтому я задаюсь вопросом: как правильно перенести такое поведение в новые составные монады, которые делают доступными слои под ними, чтобы избежать ненужного подъема и держать вещи ясными и хорошо организованными.

ответ

9

Обычный шаблон - это определение нового типа для полного стека трансформатора.

data A = A 
data E = E 

newtype MyBranchT m a = MyBranchT { getMyBranchT :: ExceptT E (StateT A m) a } 

Если есть какие-либо части стека, которые добавляют значимые новые возможности самостоятельно вы бы также определить Newtypes для этих частей.

Затем вы используете GeneralizedNewtypeDeriving для получения экземпляров для всех различных классов монады.

{-# LANGUAGE GeneralizedNewtypeDeriving #-} 

-- base 
import Control.Applicative 
import Control.Monad 

-- transformers 
import Control.Monad.IO.Class 
import Control.Monad.Trans.Class 
import Control.Monad.Trans.Except 
import Control.Monad.Trans.State.Lazy 

-- mtl classes 
import Control.Monad.Cont.Class 
import Control.Monad.Error.Class 
import Control.Monad.Reader.Class 
import Control.Monad.State.Class 
import Control.Monad.Writer.Class 

data A = A 
data E = E 

newtype MyBranchT m a = MyBranchT { getMyBranchT :: ExceptT E (StateT A m) a } 
    deriving (Functor, Applicative, Monad, MonadIO,  -- classes from base and transformers 
       {- Alternative, MonadPlus, -}    -- if E is a monoid 
       MonadState A, MonadError E,    -- classes from mtl that MyBranchT provides 
       MonadCont, MonadReader r, MonadWriter w) -- classes from mtl that might be available from m 

Вы должны будете написать экземпляр MonadTrans вручную. lift всегда только lift один раз для каждого из трансформаторов в стеке.

instance MonadTrans MyBranchT where 
    lift = MyBranchT . lift . lift 

Если есть какая-либо части стека, которые добавляют значимые новые возможности X самостоятельно вы бы также определить новый MonadX класс для тех возможностей, писать MonadX экземпляров для каждого из монад (StateT трансформаторов для, ExceptT , ContT и т. Д.) И получить экземпляр MonadX для вашего стека трансформатора (MyBranchT).

Вы также обычно делают синоним типа для MyBranchT Identity и функции runMyBranchT и runMyBranch

import Data.Functor.Identity 

type MyBranch a = MyBranchT Identity a 

runMyBranchT :: MyBranchT m a -> A -> m (Either E a, A) 
runMyBranchT mb s = runStateT (runExceptT (getMyBranchT mb)) s 

runMyBranch :: MyBranch a -> A -> (Either E a, A) 
runMyBranch mb s = runIdentity $ runMyBranchT mb s 
+0

Большое спасибо, это отличный ответ. Могу ли я как-то обойтись без языковых расширений, таких как 'GeneralizedNewtypeDeriving'? – jakubdaniel

+0

@JakubDaniel Конечно, вы можете написать все экземпляры вручную. Но вы не хотите (и, вероятно, повредите), таким образом, «GeneralizedNewtypeDeriving». – Cirdec

+0

Чтобы увидеть фактический код, который выводит выполнение команды ': set -ddump-deriv' в GHCi, он показывает вам весь код, который GHC щедро пишет для вас. 'данные AB = A | B, вызывая Eq 'dumps' instance GHC.Classes.Eq Ghci5.AB где (GHC.Classes. ==) Ghci5.A Ghci5.A = GHC.Types.True; (GHC.Classes. ==) Ghci5.B Ghci5.B = GHC.Types.True; (GHC.Classes. ==) _ _ = GHC.Types.False' –

 Смежные вопросы

  • Нет связанных вопросов^_^