Skip to content

Commit 197bbeb

Browse files
authored
Merge pull request #11 from purescript/documentation
Documentation (from docs repo). Fixes #5
2 parents 4ffec51 + 13a9f12 commit 197bbeb

File tree

2 files changed

+164
-6
lines changed

2 files changed

+164
-6
lines changed

README.md

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,157 @@ bower install purescript-effect
1313

1414
## Documentation
1515

16+
Values in PureScript do not have side-effects by default. This package provides
17+
the standard type PureScript uses to handle "native" effects, i.e. effects
18+
which are provided by the runtime system, and which cannot be emulated by pure
19+
functions. Some examples of native effects are:
20+
21+
* Console IO
22+
* Random number generation
23+
* Exceptions
24+
* Reading/writing mutable state
25+
26+
And in the browser:
27+
28+
* DOM manipulation
29+
* XMLHttpRequest / AJAX calls
30+
* Interacting with a websocket
31+
* Writing/reading to/from local storage
32+
33+
All of these things may be represented in PureScript by the `Effect` type.
34+
A value of the type `Effect a` represents a computation which may perform
35+
some native effects, and which produces a value of the type `a` once it
36+
finishes. For example, the module `Effect.Random` from `purescript-random`
37+
exports a value `random`, whose type is `Effect Number`. When run, this
38+
effect produces a random number between 0 and 1. To give another example,
39+
the module `Effect.Console` exports a function `log`, whose type is
40+
`String -> Effect Unit`. This function takes a string and produces an
41+
effect which, when run, prints the provided string to the console.
42+
43+
```purescript
44+
module RandomExample where
45+
import Prelude
46+
import Effect (Effect)
47+
import Effect.Random (random)
48+
import Effect.Console (log)
49+
50+
printRandom :: Effect Unit
51+
printRandom = do
52+
n <- random
53+
log (show n)
54+
```
55+
56+
In this example, `printRandom` is an effect which generates a random
57+
number between 0 and 1 and prints it to the console. You can test it out
58+
in the repl:
59+
60+
```
61+
> import RandomExample
62+
> printRandom
63+
0.71831842260513870
64+
unit
65+
```
66+
67+
The `unit` is there because all effects must produce a value. When there
68+
is nothing useful to return, we usually use the type `Unit`, which has
69+
just one value: `unit`.
70+
71+
### Effects and Purity
72+
73+
Note that using `Effect` does not compromise the purity of your program.
74+
Functions which make use of `Effect` are still pure, in the sense that
75+
all functions will return the same outputs given the same inputs.
76+
77+
This is enabled by having the `Effect` type denote _values_ which can be
78+
run to produce native effects; an `Effect a` is a computation which has
79+
not yet necessarily been performed. In fact, there is no way of "running"
80+
an `Effect` manually; we cannot provide a (safe) function of the type
81+
`forall a. Effect a -> a`, because such a function would violate purity;
82+
it could produce different outputs given the same input. (Note that there
83+
is actually such a function in the module `Effect.Unsafe`, but it is best
84+
avoided outside of exceptional circumstances.)
85+
86+
Instead, the recommended way of "running" an effect is to include it as
87+
part of your program's `main` value.
88+
89+
A consequence of being able to represent native effects purely using
90+
`Effect` is that you can construct and pass `Effect` values around your
91+
programs freely. For example, we can write functions which can decide
92+
whether to run a given effect just once, many times, or not at all:
93+
94+
```purescript
95+
-- | Runs an effect three times, provided that the given condition is
96+
-- | true.
97+
thriceIf :: Boolean -> Effect Unit -> Effect Unit
98+
thriceIf cond effect =
99+
if cond
100+
then do
101+
effect
102+
effect
103+
effect
104+
else
105+
pure unit
106+
```
107+
108+
### Using Effects via the Foreign Function Interface
109+
110+
A computation of type `Effect a` is implemented in JavaScript as a
111+
zero-argument function whose body is expected to perform its side-effects
112+
before finally returning its result. For example, suppose we wanted a
113+
computation which would increment a global counter and return the new
114+
result each time it was run. We can implement this as follows:
115+
116+
```purescript
117+
-- Counter.purs
118+
foreign import incrCounter :: Effect Int
119+
```
120+
121+
and in the corresponding JavaScript module:
122+
123+
```javascript
124+
// Counter.js
125+
exports.incrCounter = function() {
126+
if (!window.globalCounter) {
127+
window.globalCounter = 0;
128+
}
129+
return ++window.globalCounter;
130+
}
131+
```
132+
133+
For more information about the FFI (Foreign Function Interface), see
134+
the [documentation repository](https://github.com/purescript/documentation).
135+
This package also provides a module
136+
[Effect.Uncurried](https://pursuit.purescript.org/packages/purescript-effect/docs/Effect.Uncurried)
137+
to simplify the process of making uncurried effectful JavaScript functions
138+
available to PureScript via the FFI.
139+
140+
### The Effect type is Magic
141+
142+
The PureScript compiler has special support for the `Effect` monad.
143+
Ordinarily, a chain of monadic binds might result in poor performance when
144+
executed. However, the compiler can generate code for the `Effect` monad
145+
without explicit calls to the monadic bind function `>>=`.
146+
147+
Take the random number generation example from above. When compiled, the
148+
compiler produces the following JavaScript:
149+
150+
```javascript
151+
var printRandom = function __do() {
152+
var $0 = Effect_Random.random();
153+
return Effect_Console.log(Data_Show.show(Data_Show.showNumber)($0))();
154+
};
155+
```
156+
157+
whereas a more naive compiler might, for instance, produce:
158+
```javascript
159+
var printRandom = Control_Bind.bind(Effect.bindEffect)(Effect_Random.random)(function ($0) {
160+
return Effect_Console.log(Data_Show.show(Data_Show.showNumber)($0));
161+
});
162+
```
163+
164+
While this is a small improvement, the benefit is greater when using
165+
multiple nested calls to `>>=`.
166+
167+
### API reference
168+
16169
Module documentation is [published on Pursuit](http://pursuit.purescript.org/packages/purescript-effect).

src/Effect.purs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
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.
14
module Effect
25
( Effect
36
, untilE, whileE, forE, foreachE
@@ -7,12 +10,9 @@ import Prelude
710

811
import Control.Apply (lift2)
912

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.
13+
-- | A native effect. The type parameter denotes the return type of running the
14+
-- | effect, that is, an `Effect Int` is a possibly-effectful computation which
15+
-- | eventually produces a value of the type `Int` when it finishes.
1616
foreign import data Effect :: Type -> Type
1717

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

3434
instance monadEffect :: Monad Effect
3535

36+
-- | The `Semigroup` instance for effects allows you to run two effects, one
37+
-- | after the other, and then combine their results using the result type's
38+
-- | `Semigroup` instance.
3639
instance semigroupEffect :: Semigroup a => Semigroup (Effect a) where
3740
append = lift2 append
3841

42+
-- | If you have a `Monoid a` instance, then `mempty :: Effect a` is defined as
43+
-- | `pure mempty`.
3944
instance monoidEffect :: Monoid a => Monoid (Effect a) where
4045
mempty = pureE mempty
4146

0 commit comments

Comments
 (0)