2015-09-16 13 views
1

Я хочу использовать Reader в своем DSL, который я создал с помощью Free monad.Как использовать экземпляр MonadReader Free?

Я заметил, что не является экземпляром MonadReader бесплатно здесь:

https://hackage.haskell.org/package/free-4.12.1/docs/src/Control-Monad-Free.html#line-264

Если я пытаюсь вызвать ask внутри программы, написанной в моем EDSL, я получаю ошибку типа «Нет такой случай MonadReader Free MyDSL "

В конечном итоге я захочу использовать другие Monads с моей DSL, такие как MonadError и Logging monad, но монада Reader является первой, которую я пробовал.

+2

Вы должны опубликовать точную ошибку. Бьюсь об заклад, это на самом деле несколько более конкретный: я уверен, он говорит, что нет экземпляра 'MonadReader' для' MyDSL', * not * для 'Free MyDSL', как вы утверждаете в вопросе. Это должен быть довольно сильный намек на то, что нужно для этого. –

+0

@ DanielWagner У меня возникли проблемы с воспроизведением точной ошибки по разным причинам, но специфика не имеет большого значения. Я просто ищу любой пример, который объединяет любую mtl-монаду с Free. –

+0

Вам понадобится «MyDSL» для экземпляра «MonadReader» для соответствующего типа среды. 'Free' не * добавляет * эффект читателя; он просто использует базовый. Если вы не можете опубликовать свой точный код и сообщение об ошибке, трудно понять, как мы можем вам помочь. – dfeuer

ответ

3

Как вы связаны выше, есть MonadReader экземпляр для Free:

instance (Functor m, MonadReader e m) => MonadReader e (Free m) where 

, что это говорит о том, что, учитывая, что m является Functor и что есть MonadReader e экземпляр для m, мы также можем использовать экземпляра MonadReader внутри Free. Но для этого требуется, что уже есть пример MonadReader для m, который в вашем случае является вашим DSL Functor. Обычно это не, что вы хотите, потому что это радикально ограничивает доступные варианты для вашего функтора DSL, учитывая, что уже недостаточно быть функтором, а также должно быть монада.

Я бы предложил вместо использования Free (ReaderT r DSL) a вы могли бы просто сложить его наоборот, то есть ReaderT r (Free DSL) a, который имеет то преимущество, что DSL должен быть только функтором. Для того, чтобы сделать это более конкретным, и учитывая, что вы не указать, как ваш DSL выглядит, давайте использовать пример Teletype DSL:

data TeletypeF a = GetChar (Char -> a) | PutChar Char a deriving Functor 

type Teletype a = Free TeletypeF a 

getChar :: Teletype Char 
getChar = liftF (GetChar id) 

putChar :: Char -> Teletype() 
putChar c = liftF (PutChar c()) 

putStrLn :: String -> Teletype() 
putStrLn str = traverse putChar str >> putChar '\n' 

runTeletype :: Teletype a -> IO a 
runTeletype = foldFree go 
    where go (GetChar k) = k <$> IO.getChar 
     go (PutChar c k) = IO.putChar c >> return k 

putStrLn программа получена из DSL примитивного PutChar. Мы можем интерпретировать программы, используя монаду IO. Теперь мы хотим использовать монадный трансформатор ReaderT, чтобы иметь возможность отложить выбор разделителя конца строки в putStrLn. Таким образом, мы поступим следующим образом:

type TeletypeReader a = ReaderT Char (Free TeletypeF) a 

getChar' :: TeletypeReader Char 
getChar' = lift getChar 

putChar' :: Char -> TeletypeReader() 
putChar' c = lift (putChar c) 

putStrLn' :: String -> TeletypeReader() 
putStrLn' str = do 
    traverse_ putChar' str 
    sep <- ask 
    putChar' sep 

runTeletypeReader :: Char -> TeletypeReader a -> IO a 
runTeletypeReader sep = runTeletype . flip runReaderT sep 

И теперь мы можем сделать:

λ> runTeletypeReader '\n' (putStrLn' "Hello" >> putStrLn' "World") 
Hello 
World 

λ> runTeletypeReader ':' (putStrLn' "Hello" >> putStrLn' "World") 
Hello:World: 
+1

Отличная работа, чтобы попасть в основной вопрос! – dfeuer

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

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