我对Haskell比较陌生.我正在happstack-lite上创建一个小的api/dsl ,它的界面更像Sinatra,主要是为了学习.作为其中的一部分,我想使用do语法构造一个数组(基本上因为它比msum [route, route, route]
事物更漂亮.monad 的用法看起来像这样:
someFunctionThatMakesStrings :: String
unwrapMyMonadAndGiveMeAList :: MyMonad _ -> [String]
makeAList :: [String]
makeAList = unwrapMyMonadAndGiveMeAList do
someFunctionThatMakesStrings
someFunctionThatMakesStrings
someFunctionThatMakesStrings
...
Run Code Online (Sandbox Code Playgroud)
所以makeAList会返回一个包含3个字符串的列表.请注意,我想在其中使用不知道monad的函数(它们只返回一个字符串).
我可以用Writer做到这一点,但是每个被调用的函数都必须知道Writer monad,它看起来也有点矫枉过正(我不需要返回类型元组,而且我让它工作的方式涉及大量的包装/展开)
我尝试使用列表monad本身,但它显然是用于不同于此的东西.
那么我的哪些假设需要改变,然后我将如何从头开始创建一个新的列表构造monad?我能接近多远?
作家绝对是你想要的; 您可以避免"暴露" Writer
到外部,但代价是定义本身需要更多开销:
foo :: [String]
foo = execWriter $ do
tell otherList1
tell otherList2
tell otherList3
otherList1 :: [String]
otherList1 = execWriter $ do
...
Run Code Online (Sandbox Code Playgroud)
也就是说,你可以在每个定义中保持Writer本地的使用,但是你必须将每个要用作"源"的列表包装起来tell
.这里的关键是使用execWriter
,它会丢弃元组的结果元素(它与之相同snd . runWriter
).
但是,如果你有很多像这样的定义,我建议你直接使用Writer,只execWriter
在你想要合并结果的地方申请; 您可以通过定义类似的同义词来使类型更清晰type Foo = Writer [String]
.
我不确定构建自己的列表创建monad会有什么好处; 它最终会变得相同Writer [String]
.
列表monad确实与你想要做的事情无关.
至于定义你自己的列表编写monad,它很简单:
data ListWriter a = ListWriter a [String]
runListWriter :: ListWriter a -> (a, [String])
runListWriter (ListWriter a xs) = (a, xs)
execListWriter :: ListWriter a -> [String]
execListWriter = snd . runListWriter
instance Monad ListWriter where
return a = ListWriter a []
ListWriter a xs >>= f = ListWriter b (xs ++ ys)
where ListWriter b ys = f a
Run Code Online (Sandbox Code Playgroud)
唯一棘手的部分是(>>=)
,我们必须只取左参数的值部分,将其输入右手参数,将其拆开,然后将两个列表合并在一起,将其包装回右边的结果手边.