我对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
...
因此makeAList将返回一个包含3个字符串的列表。请注意,我想在其中使用不知道monad的函数(它们只返回一个字符串)。
我可以用Writer做到这一点,但是每个被调用的函数都必须知道Writer monad,它看起来也有点矫枉过正(我不需要返回类型元组,以及我让它工作的方式涉及很多包装/展开)
我尝试使用列表monad本身,但它显然是用于不同于此的东西。
那么我的哪些假设需要改变,然后我将如何从头开始创建一个新的列表构造monad?我能接近多少?
答案 0 :(得分:6)
作家绝对是你想要的;您可以避免将Writer
“暴露”到外部,但代价是定义本身需要更多开销:
foo :: [String]
foo = execWriter $ do
tell otherList1
tell otherList2
tell otherList3
otherList1 :: [String]
otherList1 = execWriter $ do
...
即,您可以在每个定义中保持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
唯一棘手的部分是(>>=)
,我们必须只取左参数的值部分,将其输入右手参数,将其分开,然后将两个列表合并在一起,包装它用右手边的结果备份。