|
1 | 1 | -- | This module provides the `Effect` type, which is used to represent |
2 | 2 | -- | _native_ effects. The `Effect` type provides a typed API for effectful |
3 | 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 `>>=`. |
151 | 4 | module Effect |
152 | 5 | ( Effect |
153 | 6 | , untilE, whileE, forE, foreachE |
|
0 commit comments