摘要Monad链中的重复应用

时间:2015-08-23 06:22:17

标签: haskell monads higher-order-functions

Haskell wikibook有一个示例,显示了在尝试在整个数据库中查找不同的连接信息时如何链接lookup命令,如下所示:

getTaxOwed :: String       -- their name
       -> Maybe Double -- the amount of tax they owe
getTaxOwed name = 
  lookup name phonebook >>=
    (\number -> lookup number governmentDatabase) >>=
      (\registration -> lookup registration taxDatabase)

并以do表示法重写:

getTaxOwed name = do
  number       <- lookup name phonebook
  registration <- lookup number governmentDatabase
  lookup registration taxDatabase

现在,每当我看到一个函数重复不止一次时,我会立即想办法抽象它重复的应用程序,但是因为我还没有在实践中使用Monads,而且它们似乎已经处于相当高的抽象层次,在这种情况下我不知道如何处理它。

有什么方法,如果有的话,编码员可以抽象出上面的共同模式,即在每一行中调用lookup

(旁白:这是“抽象结束”一词的适当背景吗?我觉得它抓住了我的意思,但我不确定,我想确保我正确地使用术语作为相对较新的编码器;我浏览了其他帖子,澄清了它的用法和含义,但我仍然无法弄清楚这个特殊的例子)

1 个答案:

答案 0 :(得分:2)

非常感谢Carsten提供foldM的链接!感谢他们对这个答案的见解。

因此,如果我们使用foldM,我们可以编写一个函数,该函数通过依赖于每个先前结果的多个目录重复执行lookup链接。如果,由于使用monads,在任何时候lookup都找不到目录中的当前key,它将终止并返回Nothing

lookupALot :: Eq a => a -> [(a,b)] -> Maybe b
lookupALot key directories = foldM lookup key directories

这有

形式的输出
  foldM f k1 [d1, d2, ..., dm] -- k == key, d == directory
  ==
  do
    k2 <- f k1 d1
    k3 <- f k2 d2
    ...
    f km dm

完全相同的结构
  do
    number       <- lookup name phonebook
    registration <- lookup number governmentDatabase
    lookup registration taxDatabase

因此,编写getTaxOwed的更紧凑方式是:

getTaxOwed :: String -> Maybe Double
getTaxOwed name = foldM lookup name [phonebook, governmentDatabase, taxDatabase]

哪种方式让我感到震惊!该行代码会找到与某个人name相关联的电话号码,然后查看governmentDatabase的{​​{1}}号码,最后查找他们的税务信息。 registration。但请注意,这仅适用于registration形式的数据,如[(a,b)]的类型所示。