这个问题与这篇文章有关
这个想法是定义一个 DSL 来操作云中的文件,并定义一个解释器的组合来处理不同的方面,例如与 REST 接口的通信和日志记录。
为了使这一点更具体,假设我们有以下定义 DSL 术语的数据结构。
data CloudFilesF a
= SaveFile Path Bytes a
| ListFiles Path ([Path] -> a)
deriving Functor
我们定义函数来构建 CloudFiles 程序如下:
saveFile :: Path -> Bytes -> Free CloudFilesF ()
saveFile path bytes = liftF $ SaveFile path bytes ()
listFiles :: Path -> Free CloudFilesF [Path]
listFiles path = liftF $ ListFiles path id
然后这个想法是用另外两个 DSL 来解释这个:
data RestF a = Get Path (Bytes -> a)
| Put Path Bytes (Bytes -> a)
deriving Functor
data Level = Debug | Info | Warning | Error deriving Show
data LogF a = Log Level String a deriving Functor
我设法使用以下类型定义了从 CloudFiles DSL 到 REST DSL 的自然转换:
interpretCloudWithRest :: CloudFilesF a -> Free RestF a
然后给出一个形式的程序:
sampleCloudFilesProgram :: Free CloudFilesF ()
sampleCloudFilesProgram = do
saveFile "/myfolder/pepino" "verde"
saveFile "/myfolder/tomate" "rojo"
_ <- listFiles "/myfolder"
return ()
可以使用 REST 调用来解释程序,如下所示:
runSampleCloudProgram =
interpretRest $ foldFree interpretCloudWithRest sampleCloudFilesProgram
当试图使用日志定义对 DSL 的解释时,问题就出现了。在我上面提到的文章中,作者定义了一个带有类型的解释器:
logCloudFilesI :: forall a. CloudFilesF a -> Free LogF ()
Free LogF a
我们定义了一个具有类型的解释器:
interpretLog :: Free LogF a -> IO ()
问题是这个解释器不能
foldFree
像我上面那样结合使用。所以问题是如何解释
Free CloudFilesF a
使用上面定义的函数logCloudfilesI
的程序interpretLog
?基本上,我正在寻找构造一个类型的函数:
interpretDSLWithLog :: Free ClouldFilesF a -> IO ()
我可以用 REST DSL 做到这一点,但我不能用 usng 做到这一点logCloudfilesI
。
在这些情况下使用免费 monad 时采取的方法是什么?请注意,问题似乎在于,对于日志记录情况,我们无法为函数 in 提供任何有意义的值ListFiles
来构建程序的延续。
然而,在作者使用的第二篇文章中,这在我当前的实现Halt
中不起作用。