Skip to content

Commit fb801e0

Browse files
committed
Updates for 0.9.1
1 parent 353c8c2 commit fb801e0

File tree

5 files changed

+95
-109
lines changed

5 files changed

+95
-109
lines changed

learn/eff/index.markdown

+41-39
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: Handling Native Effects with the Eff Monad
33
author: Phil Freeman
4-
published: 2015-07-16
4+
published: 2016-05-24
55
---
66

77
In this post, I'm going to talk about PureScript's hybrid approach to handling side-effects.
@@ -35,22 +35,22 @@ import Prelude
3535

3636
import Control.Monad.Eff
3737
import Control.Monad.Eff.Random (random)
38-
import Control.Monad.Eff.Console (print)
38+
import Control.Monad.Eff.Console (logShow)
3939

4040
printRandom = do
4141
n <- random
42-
print n
42+
logShow n
4343
```
4444

4545
This example requires the [`purescript-console`](https://pursuit.purescript.org/packages/purescript-console/) and [`purescript-random`](https://pursuit.purescript.org/packages/purescript-random/) dependencies to be installed:
4646

4747
pulp init
4848
bower install --save purescript-console purescript-random
4949

50-
If you save this file as `RandomExample.purs`, you will be able to compile and run it using PSCi:
50+
If you save this file as `src/RandomExample.purs`, you will be able to compile and run it using PSCi:
5151

5252
pulp psci
53-
53+
5454
> import RandomExample
5555
> printRandom
5656
...
@@ -62,8 +62,9 @@ This program uses `do`-notation to combine two types of native effects provided
6262
#### Extensible Records and Extensible Effects
6363

6464
We can inspect the type of `printRandom` by using the `:type command`
65-
66-
> :type RandomExample.main
65+
66+
> import RandomExample
67+
> :type main
6768

6869
The type of `main` will be printed to the console. You should see a type which looks like this:
6970

@@ -74,12 +75,12 @@ This type looks quite complicated, but is easily explained by analogy with PureS
7475
Consider a simple function which uses extensible records:
7576

7677
``` haskell
77-
fullName person = person.firstName ++ " " ++ person.lastName
78+
fullName person = person.firstName <> " " <> person.lastName
7879
```
7980

8081
This function creates a full name string from an object containing `firstName` and `lastName` properties. If you find the type of this function in PSCi as before, you will see this:
8182

82-
forall t. { firstName :: String, lastName :: String | t } -> String
83+
forall t. { firstName :: String, lastName :: String | t } -> String
8384

8485
The readable version of this type is "`fullName` takes an object with `firstName` and `lastName` properties _and any other properties_ and returns a `String`".
8586

@@ -90,7 +91,7 @@ That is, `fullName` does not care if you pass an object with _more_ properties,
9091
Phil Freeman
9192
```
9293

93-
Similarly, the type of `printRandom` above can be interpreted as follows: "`printRandom` is a side-effecting computation, which can be run in any environment which supports random number generation and console IO, _and any other types of side effect_, and which yields a value of type `Unit`".
94+
Similarly, the type of `printRandom` above can be interpreted as follows: "`printRandom` is an effectful computation, which can be run in any environment which supports random number generation and console IO, _and any other types of side effect_, and which yields a value of type `Unit`".
9495

9596
This is the origin of the name "extensible effects": we can always _extend_ the set of side-effects, as long as we can support the set of effects that we need.
9697

@@ -106,21 +107,21 @@ which is _not_ the same as the type of `main`.
106107

107108
However, we can instantiate the polymorphic type variable in such a way that the types do match. If we choose `e1 ~ (console :: CONSOLE | e)`, then the two rows are equal, up to reordering.
108109

109-
Similarly, `print` has a type which can be instantiated to match the type of `printRandom`:
110+
Similarly, `logShow` has a type which can be instantiated to match the type of `printRandom`:
110111

111-
forall a e2. (Show a) => a -> Eff (console :: CONSOLE | e2) Unit
112+
forall a e2. Show a => a -> Eff (console :: CONSOLE | e2) Unit
112113

113114
This time we have to choose `e2 ~ random :: RANDOM | e`.
114115

115-
The key is that we don't have to give a type for `printRandom` in order to be able to find these substitutions. `psc` will find a most general type for `printRandom` given the polymorphic types of `random` and `print`.
116+
The key is that we don't have to give a type for `printRandom` in order to be able to find these substitutions. `psc` will find a most general type for `printRandom` given the polymorphic types of `random` and `logShow`.
116117

117118
#### Aside: The Kind of Eff
118119

119120
Looking at the [source code](https://github.com/purescript/purescript-eff/blob/master/src/Control/Monad/Eff.purs), you will see the following definition for `Eff`:
120121

121122
```
122123
foreign import Eff :: # ! -> * -> *
123-
```
124+
```
124125

125126
`*` is the usual kind of types, and `!` is the kind of effects. The `#` kind constructor is used to construct kinds for _rows_, i.e. unordered, labelled collections.
126127

@@ -138,23 +139,25 @@ If we annotate the previous example with a _closed_ row of effects:
138139
main :: Eff (console :: CONSOLE, random :: RANDOM) Unit
139140
main = do
140141
n <- random
141-
print n
142+
logShow n
142143
```
143144

144145
(note the lack of a type variable here), then we cannot accidentally include a subcomputation which makes use of a different type of effect. This is an advantage of `Eff` over Haskell's more coarsely-grained `IO` monad.
145146

146147
#### Handlers and Actions
147148

148-
Rows of effects can also appear on the left-hand side of a function arrow. This is what differentiates actions like `print` and `random` from effect _handlers_.
149+
Rows of effects can also appear on the left-hand side of a function arrow. This is what differentiates actions like `logShow` and `random` from effect _handlers_.
149150

150151
While actions _add_ to the set of required effects, a handler `subtracts` effects from the set.
151152

152153
Consider `catchException` from the [`purescript-exceptions`](https://pursuit.purescript.org/packages/purescript-exceptions/) package:
153154

154155
``` haskell
155-
catchException :: forall a e. (Error -> Eff e a) ->
156-
Eff (err :: EXCEPTION | e) a ->
157-
Eff e a
156+
catchException
157+
:: forall a e
158+
. (Error -> Eff e a)
159+
-> Eff (err :: EXCEPTION | e) a
160+
-> Eff e a
158161
```
159162

160163
Note that the type of the effect on the right of the final function arrow requires _fewer_ effects than the effect to its left. Namely, `catchException` _removes_ the `EXCEPTION` effect from the set of required effects.
@@ -165,8 +168,8 @@ For example, we can write a piece of code which uses exceptions, then wrap that
165168

166169
`purescript-eff` also defines the handler `runPure`, which takes a computation with _no_ side-effects, and safely evaluates it as a pure value:
167170

168-
type Pure a = forall e. Eff e a
169-
171+
type Pure a = Eff () a
172+
170173
runPure :: forall a. Pure a -> a
171174

172175
For example, we can define a version of the division function for which division by zero results in an exception:
@@ -175,12 +178,12 @@ For example, we can define a version of the division function for which division
175178
module ErrorsExample where
176179

177180
import Prelude
178-
import Control.Monad.Eff
179-
import Control.Monad.Eff.Exception
181+
import Control.Monad.Eff (Eff)
182+
import Control.Monad.Eff.Exception (EXCEPTION, throw)
180183

181184
divide :: forall e. Int -> Int -> Eff (err :: EXCEPTION | e) Int
182185
divide _ 0 = throw "Division by zero"
183-
divide n m = return (n / m)
186+
divide n m = pure (n / m)
184187
```
185188

186189
If we have already defined this function, we can use the `runPure` and `catchException` handlers to define a version of `divide` which reports its errors using `Either`:
@@ -198,7 +201,7 @@ Note that _after_ we use `catchException` to remove the `EXCEPTION` effect, ther
198201

199202
New effects can be defined using `foreign import data` just as in the case of types.
200203

201-
Suppose we wanted to define an effect for incrementing a single shared global counter. We simply declare the kind of our new type constructor to be `!`:
204+
Suppose we wanted to define an effect for incrementing a single shared global counter. We simply declare the kind of our new type constructor to be `!`:
202205

203206
```
204207
foreign import data COUNTER :: !
@@ -240,17 +243,17 @@ exports.unsafeRunCounter = function(f) {
240243

241244
#### The Eff Monad is Magic
242245

243-
The `psc` compiler has special support for the `Eff` monad. Ordinarily, a chain of monadic binds might result in poor performance when executed in `nodejs` or in the browser. However, the compiler can generate code for the `Eff` monad without explicit calls to the monadic bind function `>>=`.
246+
The `psc` compiler has special support for the `Eff` monad. Ordinarily, a chain of monadic binds might result in poor performance when executed in Node or in the browser. However, the compiler can generate code for the `Eff` monad without explicit calls to the monadic bind function `>>=`.
244247

245248
Take the random number generation from the start of the post. If we compile this example without optimizations, we end up the following Javascript:
246249

247250
``` javascript
248-
var main =
251+
var main =
249252
Prelude[">>="]
250253
(Control_Monad_Eff.monadEff())
251254
(Control_Monad_Eff_Random.random)
252255
(function (n) {
253-
return Control_Monad_Eff_Console.print(Prelude.showNumber())(n);
256+
return Control_Monad_Eff_Console.logShow(Prelude.showNumber())(n);
254257
});
255258
```
256259

@@ -259,7 +262,7 @@ However, if we use the default optimizations, the calls to `Eff`'s monadic bind
259262
``` javascript
260263
var main = function __do() {
261264
var n = Control_Monad_Eff_Random.random();
262-
return Control_Monad_Eff_Console.print(Prelude.showNumber())(n)();
265+
return Control_Monad_Eff_Console.logShow(Prelude.showNumber())(n)();
263266
};
264267
```
265268

@@ -269,7 +272,7 @@ The improvement is even more marked when optimizations are used in conjunction w
269272

270273
``` haskell
271274
go n = do
272-
print n
275+
logShow n
273276
go (n + 1)
274277

275278
main = go 1
@@ -278,10 +281,10 @@ main = go 1
278281
Without optimizations, the compiler generates the following Javascript, which fails after a few iterations with a stack overflow:
279282

280283
``` javascript
281-
var go =
284+
var go =
282285
Prelude[">>="]
283286
(Control_Monad_Eff.monadEff())
284-
(Control_Monad_Eff_Console.print(Prelude.showNumber())(n))
287+
(Control_Monad_Eff_Console.logShow(Prelude.showNumber())(n))
285288
(function (_) {
286289
return go(n + 1);
287290
});
@@ -294,7 +297,7 @@ var go = function (__copy_n) {
294297
return function __do() {
295298
var n = __copy_n;
296299
tco: while (true) {
297-
Control_Monad_Eff_Console.print(Prelude.showNumber())(n)();
300+
Control_Monad_Eff_Console.logShow(Prelude.showNumber())(n)();
298301
var __tco_n = n + 1;
299302
n = __tco_n;
300303
continue tco;
@@ -314,11 +317,11 @@ collatz :: Int -> Int
314317
collatz n = pureST do
315318
r <- newSTRef n
316319
count <- newSTRef 0
317-
untilE $ do
318-
modifySTRef count $ (+) 1
320+
untilE do
321+
modifySTRef count (_ + 1)
319322
m <- readSTRef r
320323
writeSTRef r $ if m `mod` 2 == 0 then m / 2 else 3 * m + 1
321-
return $ m == 1
324+
pure (m == 1)
322325
readSTRef count
323326
```
324327

@@ -337,14 +340,13 @@ var collatz = function (n) {
337340
var m = r;
338341
r = (m % 2 === 0) ? m / 2 : 3 * m + 1;
339342
return m === 1;
340-
})()) {
341-
};
343+
})()) { };
342344
return {};
343345
})();
344346
return count;
345347
});
346348
};
347-
```
349+
```
348350

349351
#### Conclusion
350352

learn/ffi/index.markdown

+10-23
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ Let's take the following simple module as an example:
1919
``` haskell
2020
module Test where
2121

22+
import Prelude
23+
2224
gcd :: Int -> Int -> Int
2325
gcd n m | n == 0 = m
2426
gcd n m | m == 0 = n
@@ -72,18 +74,6 @@ generates the following Javascript:
7274
var example$prime = 100;
7375
```
7476

75-
This scheme also applies to names of infix operators:
76-
77-
``` haskell
78-
(%) a b = ...
79-
```
80-
81-
generates
82-
83-
``` javascript
84-
var $percent = function(a) { ... }
85-
```
86-
8777
#### Calling Javascript from PureScript
8878

8979
Javascript values and functions can be used from PureScript by using the FFI. The problem becomes how to choose suitable types for values originating in Javascript.
@@ -94,22 +84,20 @@ The general rule regarding types is that you can enforce as little or as much ty
9484

9585
In PureScript, JavaScript code is wrapped using a _foreign module_. A foreign module is just a CommonJS module which is associated with a PureScript module. Foreign modules are required to adhere to certain conventions:
9686

97-
- The module must contain a comment of the form `// module ModuleName`, which associates the foreign module with its companion PureScript module.
87+
- The name of the foreign module must be the same as its companion PureScript module, with its extension changed to `.js`. This associates the foreign module with the PureScript module.
9888
- All exports must be of the form `exports.name = value;`, specified at the top level.
9989

10090
Here is an example, where we export a function which computes interest amounts from a foreign module:
10191

10292
```javascript
10393
"use strict";
10494

105-
// module Interest
106-
10795
exports.calculateInterest = function(amount) {
10896
return amount * 0.1;
10997
};
11098
```
11199

112-
This file should be saved as `src/Interest.js`. The corresponding PureScript module `Interest` will be saved in `src/Interest.purs` (these filenames are merely conventions, but are used by certain tools, such as the Pulp build tool), and will look like this:
100+
This file should be saved as `src/Interest.js`. The corresponding PureScript module `Interest` will be saved in `src/Interest.purs`, and will look like this:
113101

114102
```purescript
115103
module Interest where
@@ -128,18 +116,18 @@ Suppose we wanted to modify our `calculateInterest` function to take a second ar
128116
```javascript
129117
"use strict";
130118

131-
// module Interest
132-
133119
exports.calculateInterest = function(amount, months) {
134120
return amount * Math.exp(0.1, months);
135121
};
136122
```
137123

138-
A correct `foreign import` declaration now should use a foreign type whose runtime representation correctly handles functions of multiple arguments. The `purescript-functions` package provides a collection of such types for function arities from 0 to 10:
124+
A correct `foreign import` declaration now should use a foreign type whose runtime representation correctly handles functions of multiple arguments. The `purescript-functions` package provides a collection of such types for function arities from 0 to 10:
139125

140126
```purescript
141127
module Interest where
142128
129+
import Data.Function (Fn2)
130+
143131
foreign import calculateInterest :: Fn2 Number Number Number
144132
```
145133

@@ -155,8 +143,6 @@ An alternative is to use curried functions in the native module, using multiple
155143
```javascript
156144
"use strict";
157145

158-
// module Interest
159-
160146
exports.calculateInterest = function(amount) {
161147
return function(months) {
162148
return amount * Math.exp(0.1, months);
@@ -179,9 +165,10 @@ For example, let's write a simple PureScript function with a constrained type, a
179165
``` haskell
180166
module Test where
181167

182-
import Data.Tuple
168+
import Prelude
169+
import Data.Tuple (Tuple(..))
183170

184-
inOrder :: forall a. (Ord a) => a -> a -> Tuple a a
171+
inOrder :: forall a. Ord a => a -> a -> Tuple a a
185172
inOrder a1 a2 | a1 < a2 = Tuple a1 a2
186173
inOrder a1 a2 = Tuple a2 a1
187174
```

learn/generic/index.markdown

+3-3
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ PureScript's generics are supported by the `purescript-generics` library, and in
1313
``` haskell
1414
class Generic a where
1515
toSignature :: Proxy a -> GenericSignature
16-
toSpine :: a -> GenericSpine
17-
fromSpine :: GenericSpine -> Maybe a
16+
toSpine :: a -> GenericSpine
17+
fromSpine :: GenericSpine -> Maybe a
1818
```
1919

2020
`Generic` defines three functions:
@@ -80,7 +80,7 @@ This is not too bad, but real-world records often contain many more fields. Let'
8080
The `purescript-foreign-generic` library defines a function `readGeneric`, with the following type:
8181

8282
``` haskell
83-
readGeneric :: forall a. (Generic a) => Options -> Foreign -> F a
83+
readGeneric :: forall a. Generic a => Options -> Foreign -> F a
8484
```
8585

8686
The `Options` type here is based on the options record from Haskell's `aeson` library. For our purposes, the default options will work, but we need to turn on the `unwrapNewtypes` option, so that our `newtype` constructor gets ignored during serialization:

0 commit comments

Comments
 (0)