Skip to content

Commit ee0b827

Browse files
committed
Documentation (from docs repo). Fixes #5
The documentation I've added here is based on the Eff guide in the documentation repo, although with some fairly major changes, since e.g. the parts about rows are no longer relevant. I've also added a new small section explaining why `Effect` doesn't compromise purity.
1 parent 4ffec51 commit ee0b827

File tree

1 file changed

+158
-6
lines changed

1 file changed

+158
-6
lines changed

src/Effect.purs

Lines changed: 158 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,153 @@
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 `>>=`.
1151
module Effect
2152
( Effect
3153
, untilE, whileE, forE, foreachE
@@ -7,12 +157,9 @@ import Prelude
7157

8158
import 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.
16163
foreign import data Effect :: Type -> Type
17164

18165
instance functorEffect :: Functor Effect where
@@ -33,9 +180,14 @@ foreign import bindE :: forall a b. Effect a -> (a -> Effect b) -> Effect b
33180

34181
instance 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.
36186
instance 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`.
39191
instance monoidEffect :: Monoid a => Monoid (Effect a) where
40192
mempty = pureE mempty
41193

0 commit comments

Comments
 (0)