You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* Introduce markdownlint
* Fix: disable commands-show-output of markdownlint
This is to ensure consistency with respect to existing notation.
text/chapter1.md:
> Commands which should be typed at the command line will be preceded by a dollar symbol:
>
> ```text
> $ spago build
> ```
Recall that functions in PureScript are _curried_. `diagonal` is a function that takes a `Number` and returns a _function_, that takes a `Number` and returns a `Number`.
@@ -403,7 +400,7 @@ which returns `Just 1000` for any array input.
403
400
This vulnerability is allowed because `(\_ -> Just 1000)` and `Just 1000` match the signatures of `(a -> Maybe a)` and `Maybe a` respectively when `a` is `Int` (based on input array).
404
401
405
402
In the more secure type signature, even when `a` is determined to be `Int` based on the input array, we still need to provide valid functions matching the signatures involving `forall x`.
406
-
The *only* option for `(forall x. Maybe x)` is `Nothing`, since a `Just` value would assume a type for `x` and will no longer be valid for all `x`. The only options for `(forall x. x -> Maybe x)` are `Just` (our desired argument) and `(\_ -> Nothing)`, which is the only remaining vulnerability.
403
+
The _only_ option for `(forall x. Maybe x)` is `Nothing`, since a `Just` value would assume a type for `x` and will no longer be valid for all `x`. The only options for `(forall x. x -> Maybe x)` are `Just` (our desired argument) and `(\_ -> Nothing)`, which is the only remaining vulnerability.
Write a JavaScript function `quadraticRootsImpl`and a wrapper `quadraticRoots ::Quadratic->PairComplex` thatusesthequadraticformulatofindtherootsofthispolynomial.Returnthetworootsasa `Pair` of `Complex` numbers.*Hint:*Usethe `quadraticRoots` wrappertopassaconstructorfor `Pair` to `quadraticRootsImpl`.
488
+
Write a JavaScript function `quadraticRootsImpl`and a wrapper `quadraticRoots ::Quadratic->PairComplex` thatusesthequadraticformulatofindtherootsofthispolynomial.Returnthetworootsasa `Pair` of `Complex` numbers._Hint:_Usethe `quadraticRoots` wrappertopassaconstructorfor `Pair` to `quadraticRootsImpl`.
492
489
493
490
1. (Medium) Write the function `toMaybe ::foralla.Undefineda->Maybea`.Thisfunctionconverts `undefined` to `Nothing` and `a` valuesto `Just`s.
494
491
@@ -675,7 +672,7 @@ unit
675
672
done waiting
676
673
```
677
674
678
-
Note that asynchronous logging in the repl just waits to print until the entire block has finished executing. This code behaves more predictably when run with `spago test` where there is a slight delay *between* prints.
675
+
Note that asynchronous logging in the repl just waits to print until the entire block has finished executing. This code behaves more predictably when run with `spago test` where there is a slight delay _between_ prints.
679
676
680
677
Let's look at another example where we return a value from a promise. This function is written with `async` and `await`, which is just syntactic sugar for promises.
681
678
@@ -715,6 +712,7 @@ unit
715
712
```
716
713
717
714
## Exercises
715
+
718
716
Exercises for the above sections are still on the ToDo list. If you have any ideas for good exercises, please make a suggestion.
719
717
720
718
## JSON
@@ -1073,7 +1071,7 @@ Left errs -> alert $ "There are " <> show (length errs) <> " validation errors."
1. (Easy) Write a wrapper for the `removeItem` method on the `localStorage` object, and add your foreign function to the `Effect.Storage` module.
1079
1077
1. (Medium) Add a "Reset" button that, when clicked, calls the newly-created `removeItem` function to delete the "person" entry from local storage.
@@ -1090,9 +1088,9 @@ In this chapter, we've learned how to work with foreign JavaScript code from Pur
1090
1088
1091
1089
For more examples, the `purescript`, `purescript-contrib` and `purescript-node` GitHub organizations provide plenty of examples of libraries which use the FFI. In the remaining chapters, we will see some of these libraries put to use to solve real-world problems in a type-safe way.
1092
1090
1093
-
# Addendum
1091
+
##Addendum
1094
1092
1095
-
## Calling PureScript from JavaScript
1093
+
###Calling PureScript from JavaScript
1096
1094
1097
1095
Calling a PureScript function from JavaScript is very simple, at least for functions with simple types.
1098
1096
@@ -1127,7 +1125,7 @@ var Test = PS.Test;
1127
1125
Test.gcd(15)(20);
1128
1126
```
1129
1127
1130
-
## Understanding Name Generation
1128
+
###Understanding Name Generation
1131
1129
1132
1130
PureScript aims to preserve names during code generation as much as possible. In particular, most identifiers which are neither PureScript nor JavaScript keywords can be expected to be preserved, at least for names of top-level declarations.
1133
1131
@@ -1157,7 +1155,7 @@ var example$prime = 100;
1157
1155
1158
1156
Where compiled PureScript code is intended to be called from JavaScript, it is recommended that identifiers only use alphanumeric characters, and avoid JavaScript keywords. If user-defined operators are provided for use in PureScript code, it is good practice to provide an alternative function with an alphanumeric name for use in JavaScript.
1159
1157
1160
-
## Runtime Data Representation
1158
+
###Runtime Data Representation
1161
1159
1162
1160
Types allow us to reason at compile-time that our programs are "correct" in some sense - that is, they will not break at runtime. But what does that mean? In PureScript, it means that the type of an expression should be compatible with its representation at runtime.
1163
1161
@@ -1179,7 +1177,7 @@ As you might expect, PureScript's arrays correspond to JavaScript arrays. But re
1179
1177
1180
1178
We've already seen that PureScript's records evaluate to JavaScript objects. Just as for functions and arrays, we can reason about the runtime representation of data in a record's fields by considering the types associated with its labels. Of course, the fields of a record are not required to be of the same type.
1181
1179
1182
-
## Representing ADTs
1180
+
###Representing ADTs
1183
1181
1184
1182
For every constructor of an algebraic data type, the PureScript compiler creates a new JavaScript object type by defining a function. Its constructors correspond to functions which create new JavaScript objects based on those prototypes.
is actually represented as a JavaScript string at runtime.This is useful for designing libraries, since newtypes provide an additional layer oftype safety, but without the runtime overhead of another function call.
1245
1243
1246
-
## Representing Quantified Types
1244
+
### Representing Quantified Types
1247
1245
1248
1246
Expressions with quantified (polymorphic) types have restrictive representations at runtime.In practice, this means that there are relatively few expressions with a given quantified type, but that we can reason about them quite effectively.
1249
1247
@@ -1282,7 +1280,7 @@ Without being able to inspect the runtime type of our function argument, our onl
1282
1280
1283
1281
A full discussion of _parametric polymorphism_ and _parametricity_ is beyond the scope of this book. Note however, that since PureScript's types are _erased_ at runtime, a polymorphic function in PureScript _cannot_ inspect the runtime representation of its arguments (without using the FFI), and so this representation of polymorphic data is appropriate.
1284
1282
1285
-
## Representing Constrained Types
1283
+
###Representing Constrained Types
1286
1284
1287
1285
Functions with a type class constraint have an interesting representation at runtime. Because the behavior of the function might depend on the type class instance chosen by the compiler, the function is given an additional argument, called a _type class dictionary_, which contains the implementation of the type class functions provided by the chosen instance.
1288
1286
@@ -1313,7 +1311,7 @@ import { showNumber } from 'Data.Show'
1313
1311
shout(showNumber)(42);
1314
1312
```
1315
1313
1316
-
## Exercises
1314
+
### Exercises
1317
1315
1318
1316
1. (Easy) What are the runtime representations of these types?
1319
1317
@@ -1326,7 +1324,7 @@ shout(showNumber)(42);
1326
1324
What can you say about the expressions which have these types?
1327
1325
1. (Medium) Try using the functions defined in the `arrays` package, calling them from JavaScript, by compiling the library using `spago build` and importing modules using the `import` function inNodeJS. _Hint_: you may need to configure the output path so that the generated ES modules are available on the NodeJS module path.
1328
1326
1329
-
## Representing Side Effects
1327
+
### Representing Side Effects
1330
1328
1331
1329
The `Effect` monad is also defined as a foreign type.Its runtime representation is quite simple - an expression oftype `Effect a` should evaluate to a JavaScript function of**no arguments**, which performs any side-effects and returns a value with the correct runtime representation for type`a`.
Copy file name to clipboardExpand all lines: text/chapter11.md
+11-10
Original file line number
Diff line number
Diff line change
@@ -39,6 +39,7 @@ For example, to provide the player name using the `-p` option:
39
39
$ spago run -a "-p Phil"
40
40
>
41
41
```
42
+
42
43
```text
43
44
$ spago bundle-app
44
45
$ node index.js -p Phil
@@ -136,7 +137,7 @@ Given the `sumArray` function above, we could use `execState` in PSCi to sum the
136
137
21
137
138
```
138
139
139
-
## Exercises
140
+
## Exercises
140
141
141
142
1. (Easy) What is the result of replacing `execState` with `runState` or `evalState` in our example above?
142
143
1. (Medium) A string of parentheses is _balanced_ if it is obtained by either concatenating zero-or-more shorter balanced
@@ -220,7 +221,7 @@ To run a computation in the `Reader` monad, the `runReader` function can be used
220
221
runReader::forallra.Readerra->r->a
221
222
```
222
223
223
-
##Exercises
224
+
## Exercises
224
225
225
226
In these exercises, we will use the `Reader` monad to build a small library for rendering documents with indentation.The"global configuration" will be a number indicating the current indentation level:
226
227
@@ -339,7 +340,7 @@ We can test our modified function in PSCi:
1. (Medium) Rewrite the `sumArray` function above using the `Writer` monad and the `Additive Int` monoid from the `monoid` package.
345
346
1. (Medium) The _Collatz_ function is defined on natural numbers `n` as `n / 2` when `n` is even, and `3 * n + 1` when `n` is odd. For example, the iterated Collatz sequence starting at `10` is as follows:
@@ -545,7 +546,7 @@ One problem with this code is that we have to use the `lift` function multiple t
545
546
546
547
Fortunately, as we will see, we can use the automatic code generation provided by type class inference to do most of this "heavy lifting" for us.
547
548
548
-
## Exercises
549
+
## Exercises
549
550
550
551
1. (Easy) Use the `ExceptT` monad transformer over the `Identity` functor to write a function `safeDivide` which divides two numbers, throwing an error (as the String "Divide by zero!") if the denominator is zero.
551
552
1. (Medium) Write a parser
@@ -712,7 +713,7 @@ We can even use `many` to fully split a string into its lower and upper case com
712
713
713
714
Again, this illustrates the power of reusability that monad transformers bring - we were able to write a backtracking parser in a declarative style with only a few lines of code, by reusing standard abstractions!
714
715
715
-
## Exercises
716
+
## Exercises
716
717
717
718
1. (Easy) Remove the calls to the `lift` function from your implementation of the `string` parser. Verify that the new implementation type checks, and convince yourself that it should.
718
719
1. (Medium) Use your `string` parser with the `some` combinator to write a parser `asFollowedByBs` which recognizes strings consisting of several copies of the string `"a"` followed by several copies of the string `"b"`.
@@ -911,7 +912,7 @@ The `runGame` function finally attaches the initial line handler to the console
1. (Medium) Implement a new command `cheat`, which moves all game items from the game grid into the user's inventory. Create a function `cheat :: Game Unit` in the `Game` module, and use this function from `game`.
917
918
1. (Difficult) The `Writer` component of the `RWS` monad is currently used for two types of messages: error messages and informational messages. Because of this, several parts of the code use case statements to handle error cases.
@@ -937,13 +938,14 @@ This is best illustrated by example. The application's `main` function is define
937
938
The first argument is used to configure the `optparse` library. In our case, we simply configure it to show the help message when the application is run without any arguments (instead of showing a "missing argument" error) by using `OP.prefs OP.showHelpOnEmpty`, but the `Options.Applicative.Builder` module provides several other options.
938
939
939
940
The second argument is the complete description of our parser program:
Here `OP.info` combines a `Parser` with a set of options for how the help message is formatted. `env <**> OP.helper` takes any command line argument `Parser` named `env` and adds a `--help` option to it automatically. Options for the help message are of type `InfoMod`, which is a monoid, so we can use the `fold` function to add several options together.
948
+
Here `OP.info` combines a `Parser` with a set of options for how the help message is formatted. `env <**> OP.helper` takes any command line argument `Parser` named `env` and adds a `--help` option to it automatically. Options for the help message are of type `InfoMod`, which is a monoid, so we can use the `fold` function to add several options together.
947
949
948
950
The interesting part of our parser is constructing the `GameEnvironment`:
949
951
@@ -955,7 +957,7 @@ The interesting part of our parser is constructing the `GameEnvironment`:
955
957
956
958
Notice how we were able to use the notation afforded by the applicative operators to give a compact, declarative specification of our command line interface. In addition, it is simple to add new command line arguments, simply by adding a new function argument to `runGame`, and then using `<*>` to lift `runGame` over an additional argument in the definition of `env`.
957
959
958
-
## Exercises
960
+
## Exercises
959
961
960
962
1. (Medium) Add a new Boolean-valued property `cheatMode` to the `GameEnvironment` record. Add a new command line flag `-c` to the `optparse` configuration which enables cheat mode. The `cheat` command from the previous exercise should be disallowed if cheat mode is not enabled.
961
963
@@ -968,4 +970,3 @@ Because we separated our implementation from the user interface, it would be pos
968
970
We have seen how monad transformers allow us to write safe code in an imperative style, where effects are tracked by the type system. In addition, type classes provide a powerful way to abstract over the actions provided by a monad, enabling code reuse. We were able to use standard abstractions like `Alternative` and `MonadPlus` to build useful monads by combining standard monad transformers.
969
971
970
972
Monad transformers are an excellent demonstration of the sort of expressive code that can be written by relying on advanced type system features such as higher-kinded polymorphism and multi-parameter type classes.
and open `html/index.html` again to see the result. You should see the three different types of shapes rendered to the canvas.
152
152
153
-
## Exercises
153
+
## Exercises
154
154
155
155
1. (Easy) Experiment with the `strokePath` and `setStrokeStyle` functions in each of the examples so far.
156
156
1. (Easy) The `fillPath` and `strokePath` functions can be used to render complex paths with a common style by using a do notation block inside the function argument. Try changing the `Rectangle` example to render two rectangles side-by-side using the same call to `fillPath`. Try rendering a sector of a circle by using a combination of a piecewise-linear path and an arc segment.
and open the `html/index.html` file. If you click the canvas repeatedly, you should see a green rectangle rotating around the center of the canvas.
360
360
361
-
## Exercises
361
+
## Exercises
362
362
363
363
1. (Easy) Write a higher-order function which strokes and fills a path simultaneously. Rewrite the `Random.purs` example using your function.
364
364
1. (Medium) Use `Random` and `Dom` to create an application which renders a circle with random position, color and radius to the canvas when the mouse is clicked.
and open `html/index.html`. You should see the Koch curve rendered to the canvas.
554
554
555
-
## Exercises
555
+
## Exercises
556
556
557
557
1. (Easy) Modify the L-system example above to use `fillPath` instead of `strokePath`. _Hint_: you will need to include a call to `closePath`, and move the call to `moveTo` outside of the `interpret` function.
558
558
1. (Easy) Try changing the various numerical constants in the code, to understand their effect on the rendered system.
0 commit comments