避免在无标签最终样式的DSL中包装monads] >

问题描述 投票:2回答:1

这里是一种小型玩具DSL,其风格为[[无标签类型的最终标签](请参阅O. Kiselyov的Typed Tagless Final Interpreters。]

class Monad m => RPCToy m where mkdir :: FilePath -> m () ls :: FilePath -> m [FilePath]
例如,此小DSL的不同特征是在本地和远程的不同平台上实现mkdirls。在所有实现中,类型m是monad,它可以是IO,或者是某些网络库提供的一个,或者是其他一些自制的monad。

这是IO中的实现:

import System.Directory (listDirectory) import Control.Monad (void) instance RPCToy IO where mkdir = void . putStrLn . ("better not create "++) ls = listDirectory

和一些应用程序

import Control.Monad (unless) demo :: RPCToy m => m () demo = do files <- ls "." unless ("test" `elem` files) $ mkdir "test"

可以在IO monad中运行

main :: IO () main = do demo

到目前为止很好。 

现在假设不同的实现依赖于相同的monad m,例如来自同一网络库。为了使键入的无标签最终样式能够工作,这里需要不同的monad,但它们本质上是相同的。可以通过包装东西来消除歧义:

{-# LANGUAGE GeneralizedNewtypeDeriving #-} newtype Local a = Local {runLocal :: IO a} deriving (Functor, Applicative, Monad)

然后执行RPCToy Local

instance RPCToy Local where mkdir = Local . putStrLn . ("BETTER NOT CREATE "++) ls = Local . listDirectory

可以很好地运行

main :: IO () main = do runLocal demo

什么是我的烦恼:

实现者必须在他们的代码中放入很多Local,或者反复地像这样包装库函数]
localListDirectory = Local . listDirectory ...
一个想法是创建一个“索引单子” im i aim i是单子,它带有索引类型i,其唯一目的是让编译器区分不同的实现。 RebindableSyntax扩展名使此操作成为可能,而不必放弃do语法。但是每个单子必须被“提升”到索引的单子中。改进之处在于:每个monad m及其功能只需提升一次。否则,它仍然非常令人费解。

我想知道是否有更好的方法来摆脱monad包装。

这里是带有类型化的无标签最终样式的小型玩具DSL(请参阅O. Kiselyov的类型化的无标签最终解释器)。 class Monad m => RPCToy m其中mkdir :: FilePath-> m()ls :: FilePath-&...

haskell monads dsl
1个回答
0
投票
[这是一种方法:引入一个monad转换器,该转换器只是通过添加幻像类型i来包装另一个monad,

import Control.Monad.Trans.Class (MonadTrans, lift) newtype IndexedWrapT i m a = IndexedWrapT {runWrapT :: m a} deriving (Functor, Applicative, Monad) instance MonadTrans (IndexedWrapT i) where lift = IndexedWrapT

© www.soinside.com 2019 - 2024. All rights reserved.