Consider the following code example, taken directly from the handbook chapter on Asynchronous Effects.
-- https://book.purescript.org/chapter9.html
module Main where
import Prelude
import Data.Either (Either(..))
import Effect.Aff (Aff, attempt, message)
import Effect.Class.Console (log)
import Node.Encoding (Encoding(..))
import Node.FS.Aff (readTextFile, writeTextFile)
import Node.Path (FilePath)
copyFile :: FilePath -> FilePath -> Aff Unit
copyFile file1 file2 = do
my_data <- readTextFile UTF8 file1
writeTextFile UTF8 file2 my_data
main :: Aff Unit
main = do
result <- attempt $ copyFile "file1.txt" "file2.txt"
case result of
Left e -> log $ "There was a problem with copyFile: " <> message e
_ -> pure unit
However, when this application is run with Spago Run, it produces the following puzzling error, which seems to resemble a JavaScript error instead of a PureScript error.
require(...).main is not a function
The questions would be a follows:
Short answer: main
has to be an Effect
, not an Aff
.
Your main
is an Aff
, and the error message is absolutely correct: Aff
is indeed not a function. It's a non-trivial data structure that represents an async computation, and it cannot be trivially "started" from JavaScript without going into way too much PureScript internals.
Effect
, on the other hand, is modeled as a parameterless function on the JavaScript side. For example, something like this:
f :: Effect Int
f = pure 42
Gets compiled to JavaScript like this:
const f = () => 42
This answer has a somewhat longer discussion of this fact with examples.
So, if you give your main
the type Effect Unit
, it would be possible to trivially invoke it from JavaScript like this:
require("output/Main/index.js").main()
Just calling a function with no parameters. Which is exactly what the runtime is trying to do.
But what if you need to do async stuff in main
?
You can start an Aff
async computation via launchAff_
, which itself has type Effect Unit
, but takes an Aff
computation as a parameter and starts it asynchronously:
main :: Effect Unit
main = launchAff_ do
result <- attempt $ copyFile "file1.txt" "file2.txt"
case result of
Left e -> log $ "There was a problem with copyFile: " <> message e
_ -> pure unit
This way, the main
function itself will call launchAff_
and terminate immediately, but the launched Aff
computation will continue, and the program won't exit until the last Aff
computation stops.