使用do语法创建列表?

Sea*_*ess 3 monads haskell

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

ehi*_*ird 6

作家绝对是你想要的; 您可以避免"暴露" 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)

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