1+ -- | This module provides the `Effect` type, which is used to represent
2+ -- | _native_ effects. The `Effect` type provides a typed API for effectful
3+ -- | computations, while at the same time generating efficient JavaScript.
4+ -- |
5+ -- | As in Haskell, values in PureScript do not have side-effects by default.
6+ -- | This module provides the standard type PureScript uses to handle "native"
7+ -- | effects, i.e. effects which are provided by the runtime system, and which
8+ -- | cannot be emulated by pure functions. Some examples of native effects are:
9+ -- |
10+ -- | * Console IO
11+ -- | * Random number generation
12+ -- | * Exceptions
13+ -- | * Reading/writing mutable state
14+ -- |
15+ -- | And in the browser:
16+ -- |
17+ -- | * DOM manipulation
18+ -- | * XMLHttpRequest / AJAX calls
19+ -- | * Interacting with a websocket
20+ -- | * Writing/reading to/from local storage
21+ -- |
22+ -- | All of these things may be represented in PureScript by the `Effect` type.
23+ -- | A value of the type `Effect a` represents a computation which may perform
24+ -- | some native effects, and which produces a value of the type `a` once it
25+ -- | finishes. For example, the module `Effect.Random` from `purescript-random`
26+ -- | exports a value `random`, whose type is `Effect Number`. When run, this
27+ -- | effect produces a random number between 0 and 1. To give another example,
28+ -- | the module `Effect.Console` exports a function `log`, whose type is
29+ -- | `String -> Effect Unit`. This function takes a string and produces an
30+ -- | effect which, when run, prints the provided string to the console.
31+ -- |
32+ -- | ```purescript
33+ -- | module RandomExample where
34+ -- | import Prelude
35+ -- | import Effect (Effect)
36+ -- | import Effect.Random (random)
37+ -- | import Effect.Console (log)
38+ -- |
39+ -- | printRandom :: Effect Unit
40+ -- | printRandom = do
41+ -- | n <- random
42+ -- | log (show n)
43+ -- | ```
44+ -- |
45+ -- | In this example, `printRandom` is an effect which generates a random
46+ -- | number between 0 and 1 and prints it to the console. You can test it out
47+ -- | in the repl:
48+ -- |
49+ -- | ```
50+ -- | > import RandomExample
51+ -- | > printRandom
52+ -- | 0.71831842260513870
53+ -- | unit
54+ -- | ```
55+ -- |
56+ -- | The `unit` is there because all effects must produce a value. When there
57+ -- | is nothing useful to return, we usually use the type `Unit`, which has
58+ -- | just one value: `unit`.
59+ -- |
60+ -- | #### Effects and Purity
61+ -- |
62+ -- | Note that using `Effect` does not compromise the purity of your program.
63+ -- | Functions which make use of `Effect` are still pure, in the sense that
64+ -- | all functions will return the same outputs given the same inputs.
65+ -- |
66+ -- | This is enabled by having the `Effect` type denote _values_ which can be
67+ -- | run to produce native effects; an `Effect a` is a computation which has
68+ -- | not yet necessarily been performed. In fact, there is no way of "running"
69+ -- | an `Effect` manually; we cannot provide a (safe) function of the type
70+ -- | `forall a. Effect a -> a`, because such a function would violate purity;
71+ -- | it could produce different outputs given the same input. (Note that there
72+ -- | is actually such a function in the module `Effect.Unsafe`, but it is best
73+ -- | avoided outside of exceptional circumstances.)
74+ -- |
75+ -- | Instead, the recommended way of "running" an effect is to include it as
76+ -- | part of your program's `main` value.
77+ -- |
78+ -- | A consequence of being able to represent native effects purely using
79+ -- | `Effect` is that you can construct and pass `Effect` values around your
80+ -- | programs freely. For example, we can write functions which can decide
81+ -- | whether to run a given effect just once, many times, or not at all:
82+ -- |
83+ -- | ```purescript
84+ -- | -- | Runs an effect three times, provided that the given condition is
85+ -- | -- | true.
86+ -- | thriceIf :: Boolean -> Effect Unit -> Effect Unit
87+ -- | thriceIf cond effect =
88+ -- | if cond
89+ -- | then do
90+ -- | effect
91+ -- | effect
92+ -- | effect
93+ -- | else
94+ -- | pure unit
95+ -- | ```
96+ -- |
97+ -- | #### Using Effects via the Foreign Function Interface
98+ -- |
99+ -- | A computation of type `Effect a` is implemented in JavaScript as a
100+ -- | zero-argument function whose body is expected to perform its side-effects
101+ -- | before finally returning its result. For example, suppose we wanted a
102+ -- | computation which would increment a global counter and return the new
103+ -- | result each time it was run. We can implement this as follows:
104+ -- |
105+ -- | ```purescript
106+ -- | -- Counter.purs
107+ -- | foreign import incrCounter :: Effect Int
108+ -- | ```
109+ -- |
110+ -- | and in the corresponding JavaScript module:
111+ -- |
112+ -- | ```javascript
113+ -- | // Counter.js
114+ -- | exports.incrCounter = function() {
115+ -- | if (!window.globalCounter) {
116+ -- | window.globalCounter = 0;
117+ -- | }
118+ -- | return ++window.globalCounter;
119+ -- | }
120+ -- | ```
121+ -- |
122+ -- | For more information about the FFI (Foreign Function Interface), see
123+ -- | the [documentation repository](https://github.com/purescript/documentation).
124+ -- |
125+ -- | #### The Effect type is Magic
126+ -- |
127+ -- | The PureScript compiler has special support for the `Effect` monad.
128+ -- | Ordinarily, a chain of monadic binds might result in poor performance when
129+ -- | executed. However, the compiler can generate code for the `Effect` monad
130+ -- | without explicit calls to the monadic bind function `>>=`.
131+ -- |
132+ -- | Take the random number generation example from above. When compiled, the
133+ -- | compiler produces the following JavaScript:
134+ -- |
135+ -- | ```javascript
136+ -- | var printRandom = function __do() {
137+ -- | var $0 = Effect_Random.random();
138+ -- | return Effect_Console.log(Data_Show.show(Data_Show.showNumber)($0))();
139+ -- | };
140+ -- | ```
141+ -- |
142+ -- | whereas a more naive compiler might, for instance, produce:
143+ -- | ```javascript
144+ -- | var printRandom = Control_Bind.bind(Effect.bindEffect)(Effect_Random.random)(function ($0) {
145+ -- | return Effect_Console.log(Data_Show.show(Data_Show.showNumber)($0));
146+ -- | });
147+ -- | ```
148+ -- |
149+ -- | While this is a small improvement, the benefit is greater when using
150+ -- | multiple nested calls to `>>=`.
1151module Effect
2152 ( Effect
3153 , untilE , whileE , forE , foreachE
@@ -7,12 +157,9 @@ import Prelude
7157
8158import Control.Apply (lift2 )
9159
10- -- | The `Effect` type constructor is used to represent _native_ effects.
11- -- |
12- -- | See [Handling Native Effects with the Effect Monad](http://www.purescript.org/learn/eff/)
13- -- | for more details.
14- -- |
15- -- | The type parameter denotes the return type of running the effect.
160+ -- | A native effect. The type parameter denotes the return type of running the
161+ -- | effect, that is, an `Effect Int` is a possibly-effectful computation which
162+ -- | eventually produces a value of the type `Int` when it finishes.
16163foreign import data Effect :: Type -> Type
17164
18165instance functorEffect :: Functor Effect where
@@ -33,9 +180,14 @@ foreign import bindE :: forall a b. Effect a -> (a -> Effect b) -> Effect b
33180
34181instance monadEffect :: Monad Effect
35182
183+ -- | The `Semigroup` instance for effects allows you to run two effects, one
184+ -- | after the other, and then combine their results using the result type's
185+ -- | `Semigroup` instance.
36186instance semigroupEffect :: Semigroup a => Semigroup (Effect a ) where
37187 append = lift2 append
38188
189+ -- | If you have a `Monoid a` instance, then `mempty :: Effect a` is defined as
190+ -- | `pure mempty`.
39191instance monoidEffect :: Monoid a => Monoid (Effect a ) where
40192 mempty = pureE mempty
41193
0 commit comments