Closed6

PureScriptで特定ディレクトリ内のファイルを列挙する

Kazuki MiyanishiKazuki Miyanishi

PureScriptの言語仕様の全貌をあまりまだ理解しきれいていないけど、練習として特定ディレクトリのファイルの列挙に挑戦してみる。

spago runでPureScriptのプログラムを実行すると、PureScriptのコードはJavaScriptに変換されてNode.jsで実行されるので、Node.js向けのモジュールを使うことができる。

特定ディレクトリのファイルを列挙するには、Node.jsのfsモジュールのreaddirが使えるが、このfsモジュールを使うためのPureScriptのモジュールとして以下の2つがあった。

node-fsは非同期処理の実行結果をコールバック関数で受け取るもので、node-fs-affは非同期処理の実行結果をPureScriptのAffで受け取るもので、Aff版の方がPureScriptらしいと言える。

purescript-node-fs-extraというものもあったけど、これはNode.jsのfsではなく、fs-extraという別物のモジュールを使うためのもののようだった。

Kazuki MiyanishiKazuki Miyanishi
spago install purescript-node-fs-aff
[error] The following packages do not exist in your package set:
[error]   - purescript-node-fs-aff

何か間違えている。

purescript-node-fs-aff - Pursuit

purescript-node-fs-aff is deprecated. Functions and values from this library were merged as-is into the purescript-node-fs library.

purescript-node-fsにマージされたっぽいし、インストール時にpurescript-の部分は不要。

spago install node-fs

Geminiにプログラムを作ってもらった。

module Main where

import Effect (Effect)
import Effect.Console (log)
import Node.FS.Aff (readdir)

main :: Effect Unit
main = do
  let directoryPath = "./"

  filesOrError <- readdir directoryPath
  case filesOrError of
    Left error -> log $ "エラーが発生しました: " <> show error
    Right files -> do
      for_ files \file -> log $ "- " <> file
spago run
Error found:
in module Main
at src/Main.purs:7:16 - 7:20 (line 7, column 16 - line 7, column 20)

  Unknown type Unit

Unitが無い?

import Preludeされていなかった。

GeminiはPureScriptは得意じゃない?

spago build
[1 of 1] Compiling Main
Error found:
in module Main
at src/Main.purs:16:5 - 16:15 (line 16, column 5 - line 16, column 15)

  Could not match type

    Either t1

  with type

    Array


while trying to match type Either t1 t2
  with type Array String
while checking that expression case filesOrError of
                                 (Left error) -> (apply log) ((...) (...))
                                 (Right files) -> (for_ files) (\file ->
                                                                  ...
                                                               )
  has type Aff t0
in value declaration main

where t0 is an unknown type
      t1 is an unknown type
      t2 is an unknown type

Eitherまわりがよくわからない。

Kazuki MiyanishiKazuki Miyanishi

Microsoft Copilotにお願いしたら以下のコードができた。

module Main where

import Prelude
import Effect (Effect)
import Effect.Aff (launchAff_)
import Effect.Console (log)
import Node.FS.Aff as FS

main :: Effect Unit
main = launchAff_ do
  let dir = "./target-directory"
  files <- FS.readdir dir
  log $ "Files in " <> dir <> ":"
  traverse_ log files
spago build
[1 of 1] Compiling Main
Error found:
in module Main
at src/Main.purs:14:3 - 14:12 (line 14, column 3 - line 14, column 12)

  Unknown value traverse_

さっきのGeminiと違ってlaunchAff_AffEffectに変換するのはやってくれている。

traverse_が無い。Data.Foldableからtraverse_をインポートする必要があるとのことなので、import Data.Foldable (traverse_)を追加。

spago build
[1 of 1] Compiling Main
Error found:
in module Main
at src/Main.purs:14:3 - 14:34 (line 14, column 3 - line 14, column 34)

  Could not match type

    Effect

  with type

    Aff


while trying to match type Effect t1
  with type Aff t0
while checking that expression (discard ((apply log) ((...) (...)))) (\$__unused ->
                                                                        (traverse_ log) files
                                                                     )
  has type Aff t0
in value declaration main

where t0 is an unknown type
      t1 is an unknown type

Microsoft Copilot曰く、Affの文脈でEffectlogを使おうとしているためのようで、import Effect.Class.Console (log)を使えば良い、とのこと。

[error] Some of your project files import modules from packages that are not in the direct dependencies of your project.
To fix this error add the following packages to the list of dependencies in your config:
- aff
- foldable-traversable
You may add these dependencies by running the following command:
spago install aff foldable-traversable

モジュール不足。

spago install aff
spago install foldable-traversable
spago build
[info] Build succeeded.

OK.

spago run
[info] Build succeeded.
(作業ディレクトリ)/output/Effect.Aff/foreign.js:530
                throw util.fromLeft(step);
                ^

[Error: ENOENT: no such file or directory, scandir './target-directory'] {
  errno: -2,
  code: 'ENOENT',
  syscall: 'scandir',
  path: './target-directory'
}

Node.js v20.17.0
[error] Running failed; exit code: 1

Node.js側でエラーが出た。

あ、"./target-directory"の固定になってた。

とりあえずカレントディレクトリにする。

let dir = "./"
spago run
[1 of 1] Compiling Main
[info] Build succeeded.
Files in ./:
.gitignore
.purs-repl
.spago
output
packages.dhall
spago.dhall
src
test

できた!

Kazuki MiyanishiKazuki Miyanishi

完成コード。

module Main where

import Prelude
import Effect (Effect)
import Effect.Aff (launchAff_)
import Effect.Class.Console (log)
import Node.FS.Aff as FS
import Data.Foldable (traverse_)

main :: Effect Unit
main = launchAff_ do
  let dir = "./"
  files <- FS.readdir dir
  log $ "Files in " <> dir <> ":"
  traverse_ log files
Kazuki MiyanishiKazuki Miyanishi

VSCodeの拡張機能 PureScript IDEでimport文の自動並び替えをして、vscode-purtyで自動整形したら以下のようになった。

module Main where

import Prelude

import Data.Foldable (traverse_)
import Effect (Effect)
import Effect.Aff (launchAff_)
import Effect.Class.Console (log)
import Node.FS.Aff as FS

main :: Effect Unit
main =
  launchAff_ do
    let
      dir = "./"
    files <- FS.readdir dir
    log $ "Files in " <> dir <> ":"
    traverse_ log files
Kazuki MiyanishiKazuki Miyanishi

Data.Foldabletraverse_について。

Data.Foldable - purescript-foldable-traversable - Pursuit

traverse_ :: forall a b f m. Applicative m => Foldable f => (a -> m b) -> f a -> m Unit

シグネチャを見てもよくわからない。

Traverse a data structure, performing some effects encoded by an Applicative functor at each value, ignoring the final result.

データ構造を走査し、最終結果を無視して、各値で Applicative 関数によってエンコードされたいくつかの効果を実行します。

filesの各値にlogを適用する、みたいな感じだろうか。ゆくゆくちゃんと学ぶ。

このスクラップは3ヶ月前にクローズされました