I was curious why
Effect a is represented by
Unit -> a under the hood. Why this level of indirection is needed?
Effect a would by just
pureE could be
bindE f a could be
The same for
ST a which is built the same way, accurate to types, under the hood.
a :: Effect Unit
a = log "hello"
would print to the console right as the window gets loaded
Effects in PureScript are delayed such that defining an Effect and running an Effect are separate. That helps with being able to run Effects multiple times and with keeping evaluation order sane. For example,
import Effect.Random (random) -- random :: Effect Number
-- would this print the same number 5 times, or 5 different random numbers?
randomNumbers :: Effect Unit
for (1 .. 5) \_ -> do
r <- random
One could imagine a language in which all Effects are run as soon as they’re defined, and if you wanted to delay some Effect, you have to make it take in some argument to turn it into a “function.” (So
random really would need to be
random :: Unit -> Effect Number in order to behave the way you expect), but that violates one of the key principles of any pure FP language: referential transparency, which means that any function call should be able to be replaced by that function’s contents and the behavior stays the same. Let’s look at an example using the imaginary language I described above
sayHi :: Effect Unit
sayHi = log "hi"
f1 = do
f2 = do
f1 would output nothing at all (presumably, a single “hi” was output when defining
f2 would output 2 "hi"s, even though the only difference between it and
f1 is we substitute the name
sayHi with the contents of
If the example was PureScript instead of this imaginary language,
f2 would be identical.
Since PureScript is a functionally pure language by design, it has to be referentially transparent.
@Adrielus @ntwilson thank you. Your answers are clear and explanatory.