purescript:混合效果和数组上下文

purescript: Mixing Effect and Array contexts

我正在尝试实现一个递归函数来删除纯脚本中的空目录。对于以下代码,我收到有关将 Effect 与 Array.

匹配的错误
module Test where

import Prelude

import Data.Array as Array
import Effect (Effect)
import Node.Buffer.Class (toArray)
import Node.FS.Stats (isDirectory)
import Node.FS.Sync as FS
import Node.Path (FilePath)
import Prim.Boolean (False)

rmEmptyDirs :: FilePath -> Effect Unit
rmEmptyDirs path = do
  stats <- FS.stat path
  if isDirectory stats then do
    files <- FS.readdir path
    if Array.length files == 0 then
      FS.rmdir path
    else do
      file <- files
      rmEmptyDirs file
  else
    pure unit

错误信息如下:

Could not match type
Effect
with type
Array
while trying to match type Effect Unit
with type Array t0
while checking that expression rmEmptyDirs file
has type Array t0
in binding group rmEmptyDirs
where t0 is an unknown type

我知道最里面的 do 块在数组上下文中。我不知道如何从对 rmEmptyDirs 的递归调用中“剥离”效果。在调用之前放置 Array.singleton $ 没有帮助。 liftEffect 与我想要的效果相反。我如何获得这个编译?

将一个上下文串联到另一个上下文的标准方法是 traverse

查看类型签名:

traverse :: forall a b m. Applicative m => (a -> m b) -> t a -> m (t b)

首先你给它一个函数 a -> m b - 在你的例子中是 rmEmptyDirsa ~ FilePath, m ~ Effect, 和 b ~ Unit。 然后你给它一些容器t(在你的情况下Array)装满a(在你的情况下FilePath)。 并且它在容器中的每个值上运行该函数,returns 同一容器充满结果值 b,整个容器包裹在上下文 m.

实际上这看起来像这样:

traverse rmEmptyDirs files

然后您还需要丢弃 unit 数组,否则编译器会抱怨您隐式丢弃了它。为此,要么将其绑定到一次性变量:

_ <- traverse rmEmptyDirs files

或者使用 void 组合子,它做同样的事情:

void $ traverse rmEmptyDirs files

另一个有用的东西是 for,它只是 traverse 翻转了参数,但是翻转的参数允许你无缝地传递一个 lambda 表达式作为参数,使整个事情看起来几乎就像来自类 C 语言的 for 语句。当你不想给你用来遍历的函数命名时非常方便:

for files \file -> do
  log $ "Recursing into " <> file
  rmEmptyDirs file

最后,不相关的提示:使用 when 组合器代替 if foo then bar else pure unit。它将允许您删除 else 分支:

when (isDirectory stat) do
  file <- FS.readDir ...
  ...

而不是 length ... == 0 使用 null:

if Array.null files then ...

对于 Array 这无关紧要,但对于许多其他容器来说 length 是 O(n) 而 null 是 O(1),所以构建习惯。