Skip to content

Commit 092e8ca

Browse files
committed
Move Callback doc into its own file
1 parent 14050f9 commit 092e8ca

File tree

3 files changed

+175
-168
lines changed

3 files changed

+175
-168
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@ Includes a router, testing utils, performance utils, more.
1717
### Also it will largely be backward-compatible so don't be afraid to get started with the latest release if you're a new user.
1818
<br>
1919

20-
##### Index
20+
##### Contents
2121

2222
- [Usage (+ Setup)](doc/USAGE.md).
23+
- [The `Callback` class](doc/CALLBACK.md).
2324
- [Live Examples & Demos](https://japgolly.github.io/scalajs-react/).
2425
- [Type Summary](doc/TYPES.md).
2526
- [Functional Programming](doc/FP.md).

doc/CALLBACK.md

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
Callback
2+
========
3+
4+
A callback is a procedure that is:
5+
* meant to be run by an event handler or React lifecycle method (as opposed to on the main thread or in a `render` method).
6+
* repeatable. It can be run more than once.
7+
* Is pure (does nothing) in its construction. If you create a `Callback` but never run it, no action or effects should occur.
8+
9+
Callbacks are represented by:
10+
* `Callback` which doesn't return a result.
11+
* `CallbackTo[A]` which returns an `A`.
12+
13+
Actually `Callback` is `CallbackTo[Unit]` with a different companion object, full of different goodies that all return `Unit`.
14+
15+
You can create callbacks in a number of ways:
16+
17+
* By wrapping your code:
18+
19+
```scala
20+
// This is Callback. It is also a CallbackTo[Unit].
21+
Callback{ println("Hello! I'll be executed later.") }
22+
23+
// This is a CallbackTo[Int].
24+
CallbackTo(123)
25+
```
26+
27+
* When your component modifies its state via `.setState` or `.modState`, you are provided a `Callback` for the operation.
28+
29+
```scala
30+
componentScope.modState(_.copy(name = newName)) // returns a Callback
31+
```
32+
33+
* Using one of the `Callback` object convenience methods
34+
35+
```scala
36+
// Convenience for calling `dom.console.log`.
37+
Callback.log("Hello Console reader.")
38+
39+
// Provides both compile-time and runtime warnings that a callback isn't implemented yet.
40+
Callback.TODO("AJAX not implemented yet")
41+
42+
// Return a pure value without doing anything
43+
CallbackTo.pure(0)
44+
```
45+
46+
`Callback` also has all kinds of useful methods and combinators. Examples:
47+
* Join callbacks together with many methods like `map`, `flatMap`, `tap`, `flatTap`, and all the squigglies that
48+
you may be used to in Haskell and inspired libraries like `*>`, `<*`, `>>`, `<<`, `>>=`, etc.
49+
* `.attempt` to catch any error in the callback and handle it.
50+
* `.async`/`.delay(n)` to run asynchronously and return a `Future`.
51+
* `.logResult` to print the callback result before returning it.
52+
* `.logDuration` to measure and log how long the callback takes.
53+
54+
There are other useful methods not listed here.
55+
<br>Have a brief look through the source:
56+
[Callback.scala](../core/src/main/scala/japgolly/scalajs/react/Callback.scala).
57+
58+
Once you have a `Callback` you can run it manually if you need, by calling `.runNow()`.
59+
It isn't necessary and you shouldn't do it because scalajs-react handles it for you to ensure things run at the right time
60+
on the right threads, but if you ever want to, you can.
61+
62+
#### Fusion via `>>`
63+
64+
The `>>` operator deserves a special mention as it's commonly useful.
65+
It's used to fuse to callbacks together sequentially.
66+
It's like a pure version of `;` which is how you sequence statements imperatively.
67+
68+
```scala
69+
def greet: Callback =
70+
Callback {
71+
println("Hello.")
72+
println("Goodbye.")
73+
}
74+
75+
// This ↑ is equivalent to this ↓
76+
77+
def greet: Callback = {
78+
val hello = Callback(println("Hello."))
79+
val bye = Callback(println("Goodbye."))
80+
hello >> bye
81+
}
82+
83+
// which is equivalent to this ↓
84+
85+
def greet: Callback = {
86+
val hello = Callback(println("Hello."))
87+
val bye = Callback(println("Goodbye."))
88+
hello.flatMap(_ => bye)
89+
}
90+
```
91+
92+
If you're wondering why `>>`, it's a convention used in various other monadic libraries.
93+
I like to read it like a pipeline where the arrows show control flow.
94+
This ↓ shows that `a` is run, then it flows right to run `b`, then flows right to run `c`.
95+
96+
```scala
97+
a >> b >> c
98+
```
99+
100+
Now let me also introduce `>>=` which is an alias for `flatMap`. As we know, `flatMap` takes an argument. The signature in a `CallbackTo[A]` is `def flatMap[B](f: A => CallbackTo[B]): CallbackTo[B]`. Now consider this example:
101+
102+
```scala
103+
a >> b >>= c
104+
```
105+
106+
It still looks like a pipeline but this time there is a `=` symbol popping out which you could imagine spits out a value.
107+
The flow is still intact (`a` to `b` to `c`) except this time we know that `c` takes the output of `b`. If we expand that example into real code, it looks like this:
108+
109+
```scala
110+
val a = Callback(println("Start"))
111+
val b = CallbackTo(300)
112+
val c = (i: Int) => Callback(println("Input = " + i))
113+
114+
val x = a >> b >>= c
115+
116+
x.runNow()
117+
// Start
118+
// Input = 300
119+
```
120+
121+
#### Monadic Learning Curve
122+
123+
Working with `Callback`/`CallbackTo` might seem odd if you're not used to capturing effects with monads, but it's a transferable skill with applications outside of `Callback` itself.
124+
125+
If you'd like to learn more about this approach, see *How to declare an imperative* by Philip Wadler.
126+
<br>A summary is available here: https://www.dcc.fc.up.pt/~pbv/aulas/tapf/slides/imperative.html
127+
128+
A direct example of how *this* connects to *that*, is the **Equational reasoning** example.
129+
<br>Say we have a callback that prints "ha" twice to display "haha".
130+
131+
```scala
132+
val haha1 = Callback(print("ha")) >> Callback(print("ha"))
133+
```
134+
135+
because `Callback` is referentially-transparent, and is the representation of an action instead of the result of an action,
136+
we can do reduce duplication by doing this:
137+
138+
```scala
139+
val ha = Callback(print("ha"))
140+
val haha2 = ha >> ha
141+
```
142+
143+
Because equational reasoning holds, both callbacks are equivalent.
144+
```
145+
scala> haha1.runNow()
146+
haha
147+
scala> haha2.runNow()
148+
haha
149+
```
150+
151+
If you're getting started and find monads hard, remember you can start by just wrapping your imperative code.
152+
So instead of:
153+
```scala
154+
def speak(): Unit = {
155+
println("Hello!")
156+
println("Goodbye...")
157+
}
158+
```
159+
you can wrap it like:
160+
```scala
161+
def speak = Callback {
162+
println("Hello!")
163+
println("Goodbye...")
164+
}
165+
```
166+
167+
Notice that we change `speak()` into `speak` as it's now pure.
168+
<br>Also be careful to call `.runNow()` on any inner callbacks if you take this approach. If you don't, scalac will just throw it away without executing it which is probably not what you want -- Scala does all kinds of nasty things when `Unit` is involved which is what `;` does between imperative statements.
169+
170+
It's actually recommended that you *not* take this approach and instead, use proper operators to combine callbacks as the compiler will be able to offer help and catch problems.
171+
172+

doc/USAGE.md

Lines changed: 1 addition & 167 deletions
Original file line numberDiff line numberDiff line change
@@ -205,173 +205,7 @@ customTag(customAttr := "hello", customStyle := "123", "bye")
205205
Callbacks
206206
=========
207207

208-
A callback is a procedure that is:
209-
* meant to be run by an event handler or React lifecycle method (as opposed to on the main thread or in a `render` method).
210-
* repeatable. It can be run more than once.
211-
* Is pure (does nothing) in its construction. If you create a `Callback` but never run it, no action or effects should occur.
212-
213-
Callbacks are represented by:
214-
* `Callback` which doesn't return a result.
215-
* `CallbackTo[A]` which returns an `A`.
216-
217-
Actually `Callback` is `CallbackTo[Unit]` with a different companion object, full of different goodies that all return `Unit`.
218-
219-
You can create callbacks in a number of ways:
220-
221-
* By wrapping your code:
222-
223-
```scala
224-
// This is Callback. It is also a CallbackTo[Unit].
225-
Callback{ println("Hello! I'll be executed later.") }
226-
227-
// This is a CallbackTo[Int].
228-
CallbackTo(123)
229-
```
230-
231-
* When your component modifies its state via `.setState` or `.modState`, you are provided a `Callback` for the operation.
232-
233-
```scala
234-
componentScope.modState(_.copy(name = newName)) // returns a Callback
235-
```
236-
237-
* Using one of the `Callback` object convenience methods
238-
239-
```scala
240-
// Convenience for calling `dom.console.log`.
241-
Callback.log("Hello Console reader.")
242-
243-
// Provides both compile-time and runtime warnings that a callback isn't implemented yet.
244-
Callback.TODO("AJAX not implemented yet")
245-
246-
// Return a pure value without doing anything
247-
CallbackTo.pure(0)
248-
```
249-
250-
`Callback` also has all kinds of useful methods and combinators. Examples:
251-
* Join callbacks together with many methods like `map`, `flatMap`, `tap`, `flatTap`, and all the squigglies that
252-
you may be used to in Haskell and inspired libraries like `*>`, `<*`, `>>`, `<<`, `>>=`, etc.
253-
* `.attempt` to catch any error in the callback and handle it.
254-
* `.async`/`.delay(n)` to run asynchronously and return a `Future`.
255-
* `.logResult` to print the callback result before returning it.
256-
* `.logDuration` to measure and log how long the callback takes.
257-
258-
There are other useful methods not listed here.
259-
<br>Have a brief look through the source:
260-
[Callback.scala](../core/src/main/scala/japgolly/scalajs/react/Callback.scala).
261-
262-
Once you have a `Callback` you can run it manually if you need, by calling `.runNow()`.
263-
It isn't necessary and you shouldn't do it because scalajs-react handles it for you to ensure things run at the right time
264-
on the right threads, but if you ever want to, you can.
265-
266-
#### Fusion via `>>`
267-
268-
The `>>` operator deserves a special mention as it's commonly useful.
269-
It's used to fuse to callbacks together sequentially.
270-
It's like a pure version of `;` which is how you sequence statements imperatively.
271-
272-
```scala
273-
def greet: Callback =
274-
Callback {
275-
println("Hello.")
276-
println("Goodbye.")
277-
}
278-
279-
// This ↑ is equivalent to this ↓
280-
281-
def greet: Callback = {
282-
val hello = Callback(println("Hello."))
283-
val bye = Callback(println("Goodbye."))
284-
hello >> bye
285-
}
286-
287-
// which is equivalent to this ↓
288-
289-
def greet: Callback = {
290-
val hello = Callback(println("Hello."))
291-
val bye = Callback(println("Goodbye."))
292-
hello.flatMap(_ => bye)
293-
}
294-
```
295-
296-
If you're wondering why `>>`, it's a convention used in various other monadic libraries.
297-
I like to read it like a pipeline where the arrows show control flow.
298-
This ↓ shows that `a` is run, then it flows right to run `b`, then flows right to run `c`.
299-
300-
```scala
301-
a >> b >> c
302-
```
303-
304-
Now let me also introduce `>>=` which is an alias for `flatMap`. As we know, `flatMap` takes an argument. The signature in a `CallbackTo[A]` is `def flatMap[B](f: A => CallbackTo[B]): CallbackTo[B]`. Now consider this example:
305-
306-
```scala
307-
a >> b >>= c
308-
```
309-
310-
It still looks like a pipeline but this time there is a `=` symbol popping out which you could imagine spits out a value.
311-
The flow is still intact (`a` to `b` to `c`) except this time we know that `c` takes the output of `b`. If we expand that example into real code, it looks like this:
312-
313-
```scala
314-
val a = Callback(println("Start"))
315-
val b = CallbackTo(300)
316-
val c = (i: Int) => Callback(println("Input = " + i))
317-
318-
val x = a >> b >>= c
319-
320-
x.runNow()
321-
// Start
322-
// Input = 300
323-
```
324-
325-
#### Monadic Learning Curve
326-
327-
Working with `Callback`/`CallbackTo` might seem odd if you're not used to capturing effects with monads, but it's a transferable skill with applications outside of `Callback` itself.
328-
329-
If you'd like to learn more about this approach, see *How to declare an imperative* by Philip Wadler.
330-
<br>A summary is available here: https://www.dcc.fc.up.pt/~pbv/aulas/tapf/slides/imperative.html
331-
332-
A direct example of how *this* connects to *that*, is the **Equational reasoning** example.
333-
<br>Say we have a callback that prints "ha" twice to display "haha".
334-
335-
```scala
336-
val haha1 = Callback(print("ha")) >> Callback(print("ha"))
337-
```
338-
339-
because `Callback` is referentially-transparent, and is the representation of an action instead of the result of an action,
340-
we can do reduce duplication by doing this:
341-
342-
```scala
343-
val ha = Callback(print("ha"))
344-
val haha2 = ha >> ha
345-
```
346-
347-
Because equational reasoning holds, both callbacks are equivalent.
348-
```
349-
scala> haha1.runNow()
350-
haha
351-
scala> haha2.runNow()
352-
haha
353-
```
354-
355-
If you're getting started and find monads hard, remember you can start by just wrapping your imperative code.
356-
So instead of:
357-
```scala
358-
def speak(): Unit = {
359-
println("Hello!")
360-
println("Goodbye...")
361-
}
362-
```
363-
you can wrap it like:
364-
```scala
365-
def speak = Callback {
366-
println("Hello!")
367-
println("Goodbye...")
368-
}
369-
```
370-
371-
Notice that we change `speak()` into `speak` as it's now pure.
372-
<br>Also be careful to call `.runNow()` on any inner callbacks if you take this approach. If you don't, scalac will just throw it away without executing it which is probably not what you want -- Scala does all kinds of nasty things when `Unit` is involved which is what `;` does between imperative statements.
373-
374-
It's actually recommended that you *not* take this approach and instead, use proper operators to combine callbacks as the compiler will be able to offer help and catch problems.
208+
Moved into [CALLBACK.md](CALLBACK.md).
375209

376210

377211
Creating Components

0 commit comments

Comments
 (0)