思路是定义一个用于操作云端文件的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的解释时,问题就出现了。在我上面提到的文章中,作者定义了一个类型为interpreter的解释器:
logCloudFilesI :: forall a. CloudFilesF a -> Free LogF ()
我们为具有类型 Free LogF a
的解释器定义了以下内容:
interpretLog :: Free LogF a -> IO ()
问题在于这个解释器无法与上面所用的
foldFree
组合使用。那么问题来了,如何使用函数logCloudfilesI
和interpretLog
对Free CloudFilesF a
进行程序解释?基本上,我正在寻找构建一个类型为以下内容的函数:interpretDSLWithLog :: Free ClouldFilesF a -> IO ()
我可以使用REST DSL来完成这个任务,但是我不能使用logCloudfilesI
。
在这些情况下使用自由单子的方法是什么?请注意,对于日志记录情况,我们无法向ListFiles
函数提供有意义的值以建立程序的连续性。在第二篇文章中,作者使用了Halt
,然而,在我的当前实现中,这并不起作用。
LogF
的角度去解释一个“Free CloudFilesF”事物,但是如果你不以某种方式将[Path]
传递到该过程中,这永远不会起作用,因为Free CloudFilesF a
的大部分都被困在函数[Path] -> a
后面,所以你无法访问它——除非你把它喂给[]
。 - MichaeldebugI :: CloudFilesF r -> Free LogF r
写成debugI (SaveFile path bytes r) = log Debug (path ++ bytes) >> return r
;debugI (ListFiles path next) = log Debug path >> return (next [])
。 - MichaelFree LogF
来解释Free CloudFilesF
不是正确的方法,但我想知道LogF
在整个过程中的位置。 - Damian NadalesCloudFilesF
解释为Sum LogF RestF
或其他内容 - 即,他在其rest解释器中添加了一些日志记录,这样他就可以编写像logRest :: CloudFilesF r -> Free (Sum LogF RestF) r
这样的内容。作为logRest cf = hoistFree InL (logCloudFilesI cf) *> hoistFree InR (interpretCloudWithRest cf)
(在此处使用Data.Functor.Sum
作为它的余积)。 - MichaelFree CloudFilesF
项目的全部重要联系 - 这是由interpretCloudWithRest
保留的,它知道如何“获取”文件。 - Michael