使用do语法创建列表?

时间:2012-01-13 16:03:38

标签: haskell monads

我对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?我能接近多少?

1 个答案:

答案 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

唯一棘手的部分是(>>=),我们必须只取左参数的值部分,将其输入右手参数,将其分开,然后将两个列表合并在一起,包装它用右手边的结果备份。