├── .gitignore
├── .prettierrc
├── README.md
├── assets
└── images
│ ├── AST.png
│ ├── CompilerPhases.jpg
│ ├── ErrorMessages.png
│ ├── Generated2.png
│ ├── JSFromAST.png
│ └── TypeInference.png
├── components
├── hocs
│ ├── withGraphQL.js
│ └── withLink.js
└── ui
│ ├── Common.js
│ ├── Footer.js
│ ├── JSONViewer.js
│ ├── Provider.js
│ └── SimpleProfilePlaceholder.js
├── deck.mdx
├── gatsby-config.js
├── lib
└── graphql
│ └── config.js
├── now.json
├── package-lock.json
├── package.json
├── slides
├── Author
│ ├── components
│ │ └── ImagesRow
│ │ │ └── index.js
│ └── index.js
├── TableOfContents
│ ├── components
│ │ └── ImagesRow
│ │ │ └── index.js
│ └── index.js
└── WhyCompiler
│ └── index.js
├── src
└── gatsby-theme-apollo
│ └── client.js
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | dist
2 | node_modules
3 | *.DS_Store*
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "es5",
3 | "printWidth": 60
4 | }
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Functional Programming, React, and Compilers with ReasonML
2 |
3 | These are the slides I gave for a talk at [this](https://www.meetup.com/Chicago-ReasonML/events/vvwszqybcgbcb) ReasonML monthly meetup.
4 |
5 | [Here](https://github.com/dylanirlbeck/re-mini-compiler) is the code for the compiler discussed in the talk.
6 |
7 | A recording of the talk can be found [here](https://www.youtube.com/watch?v=D_ybZoJKQSE). Please let me know your feedback!
8 |
9 | ---
10 |
11 | This project was made with MDX Deck and Code Surfer, and the compiler implementation was inspired by Gary Bernhardt's introductory course on compilers (Destroy All Software), which can be found [here](https://www.destroyallsoftware.com/screencasts/catalog/a-compiler-from-scratch).
12 |
--------------------------------------------------------------------------------
/assets/images/AST.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dylanirlbeck/fp-react-compilers/aa94de7a7eaa7520dc8dd7413fd7f7e52d30a531/assets/images/AST.png
--------------------------------------------------------------------------------
/assets/images/CompilerPhases.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dylanirlbeck/fp-react-compilers/aa94de7a7eaa7520dc8dd7413fd7f7e52d30a531/assets/images/CompilerPhases.jpg
--------------------------------------------------------------------------------
/assets/images/ErrorMessages.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dylanirlbeck/fp-react-compilers/aa94de7a7eaa7520dc8dd7413fd7f7e52d30a531/assets/images/ErrorMessages.png
--------------------------------------------------------------------------------
/assets/images/Generated2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dylanirlbeck/fp-react-compilers/aa94de7a7eaa7520dc8dd7413fd7f7e52d30a531/assets/images/Generated2.png
--------------------------------------------------------------------------------
/assets/images/JSFromAST.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dylanirlbeck/fp-react-compilers/aa94de7a7eaa7520dc8dd7413fd7f7e52d30a531/assets/images/JSFromAST.png
--------------------------------------------------------------------------------
/assets/images/TypeInference.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dylanirlbeck/fp-react-compilers/aa94de7a7eaa7520dc8dd7413fd7f7e52d30a531/assets/images/TypeInference.png
--------------------------------------------------------------------------------
/components/hocs/withGraphQL.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { ApolloProvider } from "react-apollo";
3 | import { ApolloProvider as ApolloProviderHooks } from "@apollo/react-hooks";
4 | import apolloClient from "../../src/gatsby-theme-apollo/client";
5 |
6 | const withGraphQL = BaseComponent => props => {
7 | return (
8 |
9 |
10 |
11 |
12 |
13 | );
14 | };
15 |
16 | export default withGraphQL;
17 |
--------------------------------------------------------------------------------
/components/hocs/withLink.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const withLink = Component => props => {
4 | const { url, ...rest } = props;
5 | return (
6 |
7 |
8 |
9 | );
10 | };
11 |
12 | export default withLink;
13 |
--------------------------------------------------------------------------------
/components/ui/Common.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled, { css } from "styled-components";
3 |
4 | const safeGet = (obj = {}, key, defaultValue) =>
5 | obj[key] || defaultValue;
6 |
7 | const alignItems = {
8 | center: "center",
9 | end: "flex-end",
10 | start: "flex-start",
11 | };
12 |
13 | const flexWrap = {
14 | nowrap: "nowrap",
15 | wrap: "wrap",
16 | };
17 |
18 | const justifyContent = {
19 | around: "space-around",
20 | between: "space-between",
21 | center: "center",
22 | end: "flex-end",
23 | start: "flex-start",
24 | };
25 |
26 | const flexMixin = css`
27 | align-items: ${({ align }) =>
28 | safeGet(alignItems, align, "flex-start")}
29 | justify-content: ${({ justify }) =>
30 | safeGet(justifyContent, justify, "flex-start")}
31 | `;
32 |
33 | const marginsAndPaddingsMixin = css`
34 | margin-bottom: ${({ mb }) => mb}
35 | margin-left: ${({ ml }) => ml}
36 | margin-right: ${({ mr }) => mr}
37 | margin-top: ${({ mt }) => mt}
38 | padding-bottom: ${({ pb }) => pb}
39 | padding-left: ${({ pl }) => pl}
40 | padding-right: ${({ pr }) => pr}
41 | padding-top: ${({ pt }) => pt}
42 | `;
43 |
44 | export const Column = styled.div`
45 | ${marginsAndPaddingsMixin}
46 | flex-direction: column;
47 | flex-grow: 1;
48 | `;
49 |
50 | export const Container = styled.div`
51 | ${marginsAndPaddingsMixin}
52 | `;
53 |
54 | export const Row = styled.div`
55 | ${flexMixin}
56 | ${marginsAndPaddingsMixin};
57 | display: flex;
58 | flex-direction: row;
59 | flex-wrap: ${({ wrap }) =>
60 | safeGet(flexWrap, wrap, "nowrap")};
61 | `;
62 |
63 | export const TouchableContainer = styled.button`
64 | ${flexMixin}
65 | ${marginsAndPaddingsMixin}
66 | `;
67 |
--------------------------------------------------------------------------------
/components/ui/Footer.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const Footer = () => (
4 |
22 | {"Hi"}
23 |
24 |
25 | {"Dylan Irlbeck"}
26 |
27 |
28 | );
29 |
30 | export default Footer;
31 |
--------------------------------------------------------------------------------
/components/ui/JSONViewer.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import {
3 | ObjectInspector,
4 | chromeLight,
5 | } from "react-inspector";
6 | import { Container } from "./Common";
7 |
8 | const JSONViewer = props => {
9 | const { data } = props;
10 |
11 | return (
12 |
19 |
29 |
30 | );
31 | };
32 |
33 | export default JSONViewer;
34 |
--------------------------------------------------------------------------------
/components/ui/Provider.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Footer from "./footer";
3 |
4 | const Provider = ({ children, theme, index, slides }) => (
5 |
13 | {children}
14 |
15 |
16 | );
17 |
18 | export default {
19 | Provider,
20 | };
21 |
--------------------------------------------------------------------------------
/components/ui/SimpleProfilePlaceholder.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Container } from "./Common";
3 |
4 | const SimpleProfilePlaceholder = () => {
5 | return (
6 |
17 |
25 |
26 | John Smith
27 |
28 | );
29 | };
30 |
31 | export default SimpleProfilePlaceholder;
32 |
--------------------------------------------------------------------------------
/deck.mdx:
--------------------------------------------------------------------------------
1 | import {
2 | CodeSurfer,
3 | CodeSurferColumns,
4 | } from "code-surfer";
5 | import {
6 | vsDark,
7 | github,
8 | nightOwl,
9 | } from "@code-surfer/themes";
10 |
11 | import { Image, Footer } from "mdx-deck";
12 |
13 | import Author from "./slides/Author";
14 | import TableOfContents from "./slides/TableOfContents";
15 | import WhyCompiler from "./slides/WhyCompiler";
16 |
17 | import AST from './assets/images/AST.png'; // with import
18 | import JSFromAST from "./assets/images/JSFromAST.png";
19 | import Generated2 from "./assets/images/Generated2.png";
20 | import CompilerPhases from "./assets/images/CompilerPhases.jpg";
21 | import ErrorMessages from "./assets/images/ErrorMessages.png";
22 | import TypeInference from "./assets/images/TypeInference.png";
23 | import { Row } from "./components/ui/Common";
24 |
25 | export const theme = nightOwl;
26 |
27 |
28 |
29 | Made with :heart: by @dylanirlbeck
30 |
31 |
32 |
33 | ## Functional Programming, React, and Compilers in ReasonML
34 |
35 | Chicago ReasonML Meetup, April 2020
36 |
37 | ---
38 |
39 |
40 |
41 | ---
42 |
43 | ## Why I'm doing this talk
44 |
45 | * I :heart: functional programming
46 |
47 | * ReasonML has made me a better developer
48 |
49 |
50 | 1. Want to share my experience from the world of academia
51 | 2. I want to help other people just get started building cool things with it
52 |
53 |
54 | ---
55 |
56 | ## What I hope you take away
57 |
58 | * An understanding of the core concepts behind functional programming
59 |
60 | * Ideas of what you might want to build in Reason
61 |
62 |
63 | 1. The concepts exist beyond the language (and how they're implemented in
64 | ReasonML)
65 | 2. We'll dive into some examples...
66 |
67 |
68 | ---
69 |
70 | ## ReasonML
71 |
72 |
73 |
74 | ---
75 |
76 | ## Reason
77 |
78 | - New syntax for OCaml that looks like (and compiles to) JS
79 |
80 | - "Mostly" functional (OCaml)
81 |
82 | - 100% type coverage, powerful type inference
83 |
84 |
85 | It's also really, really fast.
86 |
87 | 1. Talk about what OCaml (why is it used in compilers)
88 |
89 | Type coverage - Every line of code
90 | Soundness - Once it compiles, the types are guaranteed to be accurate
91 |
92 |
93 |
94 | ---
95 |
96 |
97 |
98 | ---
99 |
100 | ## [The Functional Approach to Programming](https://www.vitalsource.com/products/the-functional-approach-to-programming-guy-cousineau-v9781107263796?duration=perpetual&utm_source=bing&utm_medium=cpc&utm_campaign=VST%20Tier%20Three%20Shopping&msclkid=3983b13c94dd139dbcf8efba3a4a3980)
101 |
102 | - Functions are first-class citizens
103 |
104 | - Pure functions
105 |
106 | - Immutability
107 |
108 | ---
109 |
110 |
111 |
112 | ```reason title="Functions are first-class citizens" subtitle="Functions are values, too."
113 | // Reason array (the annotation is not needed!)
114 | let arr: array(int) = [|1, 2, 3|];
115 |
116 | // This is a function
117 | let double = elem => elem*2;
118 |
119 | let doubledArr = Array.map(double, arr); // [|2, 4, 6|]
120 | ```
121 |
122 |
123 |
124 |
125 | ---
126 |
127 |
128 |
129 | ```reason 1 title="Functions are first-class citizens cont'd" subtitle="Partial Application"
130 | let add = (x, y) => x + y;
131 |
132 | let addFive = add(5);
133 |
134 | let eleven = addFive(6); // 11
135 |
136 | let twelve = addFive(7); // 12
137 | ```
138 |
139 | ```reason 3 title="Functions are first-class citizens cont'd" subtitle="Partial Application"
140 | let add = (x, y) => x + y;
141 |
142 | let addFive = add(5);
143 |
144 | let eleven = addFive(6); // 11
145 |
146 | let twelve = addFive(7); // 12
147 | ```
148 |
149 | ```reason 5:7 title="Functions are first-class citizens cont'd" subtitle="Partial Application"
150 | let add = (x, y) => x + y;
151 |
152 | let addFive = add(5);
153 |
154 | let eleven = addFive(6); // 11
155 |
156 | let twelve = addFive(7); // 12
157 | ```
158 |
159 |
160 |
161 |
162 | Currying and partial application are also conflated
163 |
164 | Partial Application: Fixing a number of arguments to a function, producing
165 | another function of a smaller arity
166 |
167 | Currying - Technique of translating the evaluation of a function that takes
168 | multiple arguments into evaluating a sequence of functions, each with a single
169 | argument
170 |
171 |
172 | add above is really syntax sugar for let add = (x) => (y) => x + y;
173 |
174 |
175 |
176 | ---
177 |
178 |
179 |
180 | ```reason title="Pure functions and side effects"
181 | // Invalid Reason
182 | let incrementValue = value => {
183 | value++
184 | };
185 |
186 | // Valid Reason
187 | let incrementValue = value => {
188 | value + 1
189 | };
190 | ```
191 |
192 |
193 |
194 |
195 | Pure Functions: No side effects occur throughout the function's
196 | execution. A side effect is basically a state change in something other than the
197 | function that's currently executing it. Also, same result for same input.
198 |
199 | Here we're trying to change the state of a variable..
200 |
201 |
202 | ---
203 |
204 |
205 |
206 | ```reason title="Immutable data" subtitle="Ask yourself: how are variables defined in mathematics?"
207 | let x = 5;
208 |
209 | // Rest of your program
210 |
211 | Js.log(x); // 5
212 | ```
213 |
214 |
215 |
216 |
217 | Variables will always be of same value + type throughout the program. This is
218 | very mathematical in nature (what does it mean to be a variable and function in
219 | mathematics).
220 |
221 |
222 | ---
223 |
224 |
225 |
226 | ---
227 |
228 |
229 |
230 |
231 | Flow - Static type checker for JavaScript
232 | Hack compiler - Hack extends PHP with static types
233 | Infer - Static analyzer for Java, C++, C, and Objective-C (for mobile apps)
234 |
235 |
236 | ---
237 |
238 | ## What's in a compiler (condensed)
239 |
240 | 1. **Lexer/Tokenizer** - Create a list of tokens from input code
241 |
242 | 2. **Parser** - Turn the tokens into an AST
243 |
244 | 3. **Code Generation** - Generate output code from the AST
245 |
246 | ---
247 |
248 | ## Define your frontend and backend (and sometimes your type system)
249 |
250 | - Frontend: Mini-C
251 |
252 | - Type System: N/A
253 |
254 | - Backend: JavaScript
255 |
256 |
257 | My implementation is based on one by Gary Benhardt (Destroy All Software)...
258 |
259 |
260 | ---
261 |
262 | ## Lexer/Tokenizer
263 |
264 | Let's define the tokens of our language
265 |
266 | ```C
267 | def f() 1 end
268 | ```
269 |
270 |
271 |
272 |
273 | ---
274 |
275 |
276 |
277 | What are our "tokens"?
278 |
279 | ```
280 | def
281 |
282 | f
283 |
284 | (
285 |
286 | )
287 |
288 | 1
289 |
290 | end
291 | ```
292 |
293 |
294 |
295 | ---
296 |
297 |
298 |
299 |
300 | ```reason title="How do we represent these tokens?" subtitle="We use what's called a variant"
301 | // This is a variant
302 | type token =
303 | | DEF // def
304 | | IDENT // f
305 | | OPAREN // (
306 | | CPAREN // )
307 | | END // end
308 | | INT // 2,3,etc
309 | | COMMA; // ,
310 | ```
311 |
312 |
313 |
314 |
315 | Tokens are the smallest pieces that are meaningful
316 |
317 | Options of a varaint: Constructors or "tags"
318 |
319 |
320 | ---
321 |
322 |
323 |
324 | How are we going to consume our input program?
325 |
326 | [Regular expressions](https://reasonml.github.io/docs/en/regular-expression#docsNav).
327 |
328 |
329 |
330 | ---
331 |
332 |
333 |
334 | ```reason title="Regular Expressions"
335 | //Tokenizer.re
336 |
337 | // This is a record
338 | type tokenType = {
339 | token,
340 | regExp: Js.Re.t,
341 | };
342 |
343 | // ^ means match beginning of input
344 | let defToken = {token: DEF, regExp: [%re "/^(\\bdef\\b)/"]};
345 | let endToken = {token: END, regExp: [%re "/^(\\bend\\b)/"]};
346 | let identToken = {token: IDENT, regExp: [%re "/^(\\b[a-zA-Z]+\\b)/"]};
347 | let intToken = {token: INT, regExp: [%re "/^(\\b[0-9]+\\b)/"]};
348 | let oparenToken = {token: OPAREN, regExp: [%re "/^(\\()/"]};
349 | let cparenToken = {token: CPAREN, regExp: [%re "/^(\\))/"]};
350 | let commaToken = {token: COMMA, regExp: [%re "/^(\\,)/"]};
351 |
352 | // These are in a necessary order, i.e. don't want def to be an identifier
353 | let tokenTypes: array(tokenType) = [|
354 | defToken,
355 | endToken,
356 | identToken,
357 | intToken,
358 | oparenToken,
359 | cparenToken,
360 | commaToken,
361 | |];
362 | ```
363 |
364 | ```reason 1:4 title="Regular expressions" subtitle="Record!"
365 | // This is a record
366 | type tokenType = {
367 | token,
368 | regExp: Js.Re.t,
369 | };
370 |
371 | // ^ means match beginning of input
372 | let defToken = {token: DEF, regExp: [%re "/^(\\bdef\\b)/"]};
373 | let endToken = {token: END, regExp: [%re "/^(\\bend\\b)/"]};
374 | let identToken = {token: IDENT, regExp: [%re "/^(\\b[a-zA-Z]+\\b)/"]};
375 | let intToken = {token: INT, regExp: [%re "/^(\\b[0-9]+\\b)/"]};
376 | let oparenToken = {token: OPAREN, regExp: [%re "/^(\\()/"]};
377 | let cparenToken = {token: CPAREN, regExp: [%re "/^(\\))/"]};
378 | let commaToken = {token: COMMA, regExp: [%re "/^(\\,)/"]};
379 |
380 | // These are in a necessary order, i.e. don't want def to be an identifier
381 | let tokenTypes: array(tokenType) = [|
382 | defToken,
383 | endToken,
384 | identToken,
385 | intToken,
386 | oparenToken,
387 | cparenToken,
388 | commaToken,
389 | |];
390 | ```
391 |
392 | ```reason 7:25 title="Regular Expressions" subtitle="Construct the regexps"
393 | // This is a record
394 | type tokenType = {
395 | token,
396 | regExp: Js.Re.t,
397 | };
398 |
399 | // ^ means match beginning of input
400 | let defToken = {token: DEF, regExp: [%re "/^(\\bdef\\b)/"]};
401 | let endToken = {token: END, regExp: [%re "/^(\\bend\\b)/"]};
402 | let identToken = {token: IDENT, regExp: [%re "/^(\\b[a-zA-Z]+\\b)/"]};
403 | let intToken = {token: INT, regExp: [%re "/^(\\b[0-9]+\\b)/"]};
404 | let oparenToken = {token: OPAREN, regExp: [%re "/^(\\()/"]};
405 | let cparenToken = {token: CPAREN, regExp: [%re "/^(\\))/"]};
406 | let commaToken = {token: COMMA, regExp: [%re "/^(\\,)/"]};
407 |
408 | // These are in a necessary order, i.e. don't want def to be an identifier
409 | let tokenTypes: array(tokenType) = [|
410 | defToken,
411 | endToken,
412 | identToken,
413 | intToken,
414 | oparenToken,
415 | cparenToken,
416 | commaToken,
417 | |];
418 | ```
419 |
420 |
421 |
422 | ---
423 |
424 | Now we throw it through a tokenizer function and...
425 |
426 |
427 |
428 |
429 | ```reason
430 | // Compiler.re
431 |
432 | let program = "def f(x,y,z) 1 end";
433 | let tokens = Tokenizer.make(program);
434 | Js.log(tokens);
435 | ```
436 |
437 |
438 |
439 | ---
440 |
441 |
442 |
443 | ```json title="Tokens!" subtitle="The integers are just the JS representation of the variants"
444 | [
445 | { token: 3397029, value: 'def' },
446 | { token: 895974096, value: 'f' },
447 | { token: -780845765, value: '(' },
448 | { token: 895974096, value: 'x' },
449 | { token: -934581835, value: ',' },
450 | { token: 895974096, value: 'y' },
451 | { token: -934581835, value: ',' },
452 | { token: 895974096, value: 'z' },
453 | { token: 86829255, value: ')' },
454 | { token: 3647695, value: '1' },
455 | { token: 3448763, value: 'end' }
456 | ]
457 | ```
458 |
459 |
460 |
461 | ---
462 |
463 | ## Any questions?
464 |
465 | ---
466 |
467 | ## On to the parser
468 |
469 | Tokens -> [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree).
470 |
471 | ---
472 |
473 | ## Abstract Syntax Tree - Integer Node
474 |
475 |
476 |
477 | How do we represent the integer literal 1?
478 |
479 | ```
480 | VALUE: 1
481 | ```
482 |
483 |
484 |
485 |
486 | What is the minimum info needed to represent an integer?
487 |
488 | Literals (or constants) are the values we write in a conventional form whose
489 | values is obvious; they also do not change in value.
490 |
491 |
492 | ---
493 |
494 | ## Abstract Syntax Tree - Definition Node
495 |
496 |
497 |
498 | How about the definition `def f(...) ... end`
499 |
500 | ```
501 | DEF
502 | NAME: "f"
503 | ARGS: [...]
504 | BODY:
505 | ...
506 | ```
507 |
508 |
509 |
510 | ---
511 |
512 |
513 |
514 | ```reason title="Types"
515 | type integerNode = {value: int}
516 | and callNode = {
517 | name: string,
518 | argExprs: array(exprNode),
519 | }
520 | and varRefNode = {value: string}
521 | and exprNode =
522 | | CallNode(callNode)
523 | | IntegerNode(integerNode)
524 | | VarRefNode(varRefNode);
525 |
526 | type defNode = {
527 | name: string,
528 | argNames: array(string),
529 | body: exprNode,
530 | };
531 |
532 | type node =
533 | | DefNode(defNode)
534 | | ExprNode(exprNode);
535 | ```
536 |
537 | ```reason 1:16 title="Types" subtitle="Internal AST nodes"
538 | type integerNode = {value: int}
539 | and callNode = {
540 | name: string,
541 | argExprs: array(exprNode),
542 | }
543 | and varRefNode = {value: string}
544 | and exprNode =
545 | | CallNode(callNode)
546 | | IntegerNode(integerNode)
547 | | VarRefNode(varRefNode);
548 |
549 | type defNode = {
550 | name: string,
551 | argNames: array(string),
552 | body: exprNode,
553 | };
554 |
555 | type node =
556 | | DefNode(defNode)
557 | | ExprNode(exprNode);
558 | ```
559 |
560 | ```reason 17:20 title="Types" subtitle="Top level AST nodes"
561 | type integerNode = {value: int}
562 | and callNode = {
563 | name: string,
564 | argExprs: array(exprNode),
565 | }
566 | and varRefNode = {value: string}
567 | and exprNode =
568 | | CallNode(callNode)
569 | | IntegerNode(integerNode)
570 | | VarRefNode(varRefNode);
571 |
572 | and defNode = {
573 | name: string,
574 | argNames: array(string),
575 | body: exprNode,
576 | };
577 |
578 | type node =
579 | | DefNode(defNode)
580 | | ExprNode(exprNode);
581 | ```
582 |
583 |
584 |
585 | ---
586 |
587 | Remember, we want to turn this;
588 |
589 | ```
590 | def f(x) 1 end
591 | ```
592 |
593 | into something like this:
594 |
595 | ```
596 | DEF
597 | NAME: "f"
598 | ARGS: ["x"]
599 | BODY:
600 | INTEGER_LITERAL
601 | VALUE: 1
602 |
603 | ```
604 |
605 | ---
606 |
607 |
608 |
609 | ```reason title="How do we parse integers?"
610 | let parseInteger = tokens => {
611 | let (integerToken, newTokens) = consume(tokens, INT);
612 | let integerNode = {value: int_of_string(integerToken.value)};
613 | (ExprNode(integerNode), newTokens);
614 | };
615 | ```
616 |
617 | ```reason 2 title="Consume the INT token"
618 | let parseInteger = tokens => {
619 | let (integerToken, newTokens) = consume(tokens, INT);
620 | let integerNode = {value: int_of_string(integerToken.value)};
621 | (ExprNode(integerNode), newTokens);
622 | };
623 | ```
624 |
625 | ```reason 3 title="Build the integerNode"
626 | let parseInteger = tokens => {
627 | let (integerToken, newTokens) = consume(tokens, INT);
628 | let integerNode = {value: int_of_string(integerToken.value)};
629 | (ExprNode(integerNode), newTokens);
630 | };
631 | ```
632 |
633 | ```reason 4 title="Construct an ExprNode"
634 | let parseInteger = tokens => {
635 | let (integerToken, newTokens) = consume(tokens, INT);
636 | let integerNode = {value: int_of_string(integerToken.value)};
637 | (ExprNode(integerNode), newTokens);
638 | };
639 | ```
640 |
641 |
642 |
643 |
644 |
645 | Consume gives us back new tokens in a safe way, validates the type is what we
646 | say it is.
647 |
648 |
649 | ---
650 |
651 |
652 |
653 | ```reason title="How do we parse expressions?"
654 | let parseExpr = tokens =>
655 | if (peek(tokens, INT, ())) {
656 | parseInteger(tokens);
657 | } else {
658 | parseCall(tokens);
659 | };
660 | ```
661 |
662 |
663 |
664 |
665 | Two options for an expression, integer or function call.
666 |
667 |
668 | ---
669 |
670 | ## Let's put it all together and create our AST
671 |
672 |
673 | ---
674 |
675 |
676 |
677 | ```reason title="Parser"
678 | // Will return a tree
679 | let make = tokens => {
680 | let rec parse = tokens => {
681 | switch (Array.length(tokens)) {
682 | | 0 => [||]
683 | | _ =>
684 | let (parsedDef, remainingTokens) = parseDef(tokens);
685 | Array.concat([[|parsedDef|], parse(remainingTokens)]);
686 | };
687 | };
688 | parse(tokens);
689 | };
690 | ```
691 |
692 | ```reason 3:10 title="Parser" subtitle="Recursive parse function"
693 | // Will return a tree
694 | let make = tokens => {
695 | let rec parse = tokens => {
696 | switch (Array.length(tokens)) {
697 | | 0 => [||]
698 | | _ =>
699 | let (parsedDef, remainingTokens) = parseDef(tokens);
700 | Array.concat([[|parsedDef|], parse(remainingTokens)]);
701 | };
702 | };
703 | parse(tokens);
704 | };
705 | ```
706 |
707 | ```reason 11 title="Parser" subtitle="Start parsing!"
708 | // Will return a tree
709 | let make = tokens => {
710 | let rec parse = tokens => {
711 | switch (Array.length(tokens)) {
712 | | 0 => [||]
713 | | _ =>
714 | let (parsedDef, remainingTokens) = parseDef(tokens);
715 | Array.concat([[|parsedDef|], parse(remainingTokens)]);
716 | };
717 | };
718 | parse(tokens);
719 | };
720 | ```
721 |
722 |
723 |
724 | ---
725 |
726 | For an input of
727 | ```
728 | def f(x) ) end
729 | ```
730 |
731 | we get this Abstract Syntax Tree:
732 |
733 | ```
734 | DefNode(
735 | {
736 | name: "f"
737 | argNames: "x"
738 | body: ExprNode(
739 | IntegerNode(
740 | {
741 | value: 1
742 | }
743 | )
744 | )
745 | }
746 | )
747 | ```
748 |
749 | ---
750 |
751 | ## Questions so far?
752 |
753 | ---
754 |
755 | ## Now we're going to generate JavaScript from our input AST
756 |
757 |
758 |
759 |
760 | ---
761 |
762 | ## Our generate function will be highly recursive, just like the parser
763 |
764 | ---
765 |
766 |
767 |
768 | ```reason title="Generate code for an expression"
769 | let rec generateExpr = exprNode => {
770 | switch (exprNode) {
771 | | CallNode(callNode) =>
772 | let name = callNode.name;
773 | let argExprs = joinArgExprs(callNode.argExprs);
774 | {j|$name ($argExprs)|j};
775 | | IntegerNode(integerNode) => string_of_int(integerNode.value)
776 | | VarRefNode(varRefNode) => varRefNode.value
777 | };
778 | };
779 | ```
780 |
781 | ```reason 3:6 title="Generate a function call"
782 | let rec generateExpr = exprNode => {
783 | switch (exprNode) {
784 | | CallNode(callNode) =>
785 | let name = callNode.name;
786 | let argExprs = joinArgExprs(callNode.argExprs);
787 | {j|$name ($argExprs)|j};
788 | | IntegerNode(integerNode) => string_of_int(integerNode.value)
789 | | VarRefNode(varRefNode) => varRefNode.value
790 | };
791 | };
792 | ```
793 |
794 | ```reason 7:8 title="Generate an integer or variable reference"
795 | let rec generateExpr = exprNode => {
796 | switch (exprNode) {
797 | | CallNode(callNode) =>
798 | let name = callNode.name;
799 | let argExprs = joinArgExprs(callNode.argExprs);
800 | {j|$name ($argExprs)|j};
801 | | IntegerNode(integerNode) => string_of_int(integerNode.value)
802 | | VarRefNode(varRefNode) => varRefNode.value
803 | };
804 | };
805 | ```
806 |
807 |
808 |
809 | ---
810 |
811 |
812 |
813 | ```reason title="Main generator function"
814 | let rec generate_ = node => {
815 | switch (node) {
816 | | DefNode(defNode) =>
817 | let {name, argNames, body} = defNode;
818 | let argNamesAsString = joinArgNames(argNames);
819 | // recursively generate body
820 | let bodyAsString = generate_(ExprNode(defNode.body));
821 | {j|function $name($argNamesAsString) { return $bodyAsString }|j};
822 | | ExprNode(exprNode) => generateExpr(exprNode)
823 | };
824 | };
825 | ```
826 |
827 | ```reason 9 title="Generate an expression"
828 | let rec generate_ = node => {
829 | switch (node) {
830 | | DefNode(defNode) =>
831 | let {name, argNames, body} = defNode;
832 | let argNamesAsString = joinArgNames(argNames);
833 | // recursively generate body
834 | let bodyAsString = generate_(ExprNode(defNode.body));
835 | {j|function $name($argNamesAsString) { return $bodyAsString }|j};
836 | | ExprNode(exprNode) => generateExpr(exprNode)
837 | };
838 | };
839 | ```
840 |
841 | ```reason 3:8 title="Generate a function definition" subtitle="Make sure to wrap the body in an ExprNode b/c I messed up the types"
842 | let rec generate_ = node => {
843 | switch (node) {
844 | | DefNode(defNode) =>
845 | let {name, argNames, body} = defNode;
846 | let argNamesAsString = joinArgNames(argNames);
847 | // recursively generate body
848 | let bodyAsString = generate_(ExprNode(defNode.body));
849 | {j|function $name($argNamesAsString) { return $bodyAsString }|j};
850 | | ExprNode(exprNode) => generateExpr(exprNode)
851 | };
852 | };
853 | ```
854 |
855 |
856 |
857 | ---
858 |
859 | ## Let's put it all together with the compiler
860 |
861 | ---
862 |
863 |
864 |
865 | ```reason
866 | // Compiler.re
867 |
868 | let compile = program => {
869 | program
870 | ->Tokenizer.make
871 | ->Parser.make
872 | ->Generator.make;
873 | };
874 |
875 | compile("def f(x) 1 end");
876 | ```
877 |
878 | ```reason 3 title="Tokenize!"
879 | let compile = program => {
880 | program
881 | ->Tokenizer.make
882 | ->Parser.make
883 | ->Generator.make;
884 | };
885 |
886 | compile("def f(x, y) add(x, y) end");
887 | ```
888 |
889 | ```reason 4 title="Parse!"
890 | let compile = program => {
891 | program
892 | ->Tokenizer.make
893 | ->Parser.make
894 | ->Generator.make;
895 | };
896 |
897 | compile("def f(x, y) add(x, y) end");
898 | ```
899 |
900 | ```reason 5 title="Generate!"
901 | let compile = program => {
902 | program
903 | ->Tokenizer.make
904 | ->Parser.make
905 | ->Generator.make;
906 | };
907 |
908 | compile("def f(x, y) add(x, y) end");
909 | ```
910 |
911 | ```reason 8 title="Compile!"
912 | let compile = program => {
913 | program
914 | ->Tokenizer.make
915 | ->Parser.make
916 | ->Generator.make;
917 | };
918 |
919 | compile("def f(x, y) add(x, y) end");
920 | ```
921 |
922 |
923 |
924 | ---
925 |
926 |
927 | ## And that's our compiler!
928 |
929 | ```reason
930 | compile("def f(x) 1 end");
931 | ```
932 |
933 |
934 |
935 | ---
936 |
937 | ### Our mini-compiler also supports arbitrary nested expressions
938 |
939 | ```reason
940 | compile("def f(x, y) add(x,add(x,y)) end")
941 | ```
942 |
943 |
944 |
945 | ---
946 |
947 | ## Recap
948 |
949 | ---
950 |
951 | ## 1. Structure of a compiler (Tokenizer -> Parser -> Generator)
952 |
953 |
954 |
955 |
956 | 1. Talk about where a type system would fit into the flow of a compiler
957 |
958 |
959 | ---
960 |
961 | ## 2. Features of Reason as a language
962 |
963 | * Variants/Pattern matching: `type something = This | That;`
964 |
965 | * Records: `type somethingElse = {value: int, name: string}`
966 |
967 | * Type Inferencing
968 |
969 | ---
970 |
971 |
972 | ## 3. Reason/OCaml Type system
973 |
974 | Type Inferencing
975 |
976 |
977 |
978 |
979 | Contrast this with JavaScript
980 |
981 |
982 | ---
983 |
984 | ## 3. Reason/OCaml Type system cont'd
985 |
986 | Error Messages
987 |
988 |
989 |
990 | ---
991 |
992 |
993 |
994 | ---
995 |
996 |
997 |
998 | ### We've talked about Reason as a language, so what can we do with it?
999 |
1000 |
1006 |
1007 |
1008 |
1009 | ---
1010 |
1011 |
1012 | ## Core concepts of React (from [Jordan Walke]() himself!)
1013 |
1014 |
1015 |
1016 | 1. Minimize application state
1017 |
1018 | 2. Minimize side effects
1019 |
1020 |
1021 |
1022 |
1023 | 1. Reason, and FP languages in general, minimize state because you can't change
1024 | state globally.
1025 | 2. Again, by definition, FP languages are mostly side-effect free.
1026 |
1027 | Jordan Walke did a talk, invented React and Reason etc.
1028 |
1029 |
1030 | ---
1031 |
1032 |
1033 |
1034 | ```reason title="Intro Example"
1035 | [@react.component]
1036 | let make = (~name) => {
1037 | let (count, setCount) = React.useState(() => 0);
1038 |
1039 |
1040 |
{React.string(name ++ " clicked " ++ string_of_int(count) ++ " times")}
1041 |
setCount(_ => count + 1)}>
1042 | {React.string("Click me")}
1043 |
1044 |
1045 | };
1046 | ```
1047 |
1048 | ```reason 2 title="Intro Example" subtitle="Named arguments (props)"
1049 | [@react.component]
1050 | let make = (~name) => {
1051 | let (count, setCount) = React.useState(() => 0);
1052 |
1053 |
1054 |
{React.string(name ++ " clicked " ++ string_of_int(count) ++ " times")}
1055 |
setCount(_ => count + 1)}>
1056 | {React.string("Click me")}
1057 |
1058 |
1059 | };
1060 | ```
1061 |
1062 | ```reason 3 title="Intro Example" subtitle="useState hook"
1063 | [@react.component]
1064 | let make = (~name) => {
1065 | let (count, setCount) = React.useState(() => 0);
1066 |
1067 |
1068 |
{React.string(name ++ " clicked " ++ string_of_int(count) ++ " times")}
1069 |
setCount(_ => count + 1)}>
1070 | {React.string("Click me")}
1071 |
1072 |
1073 | };
1074 | ```
1075 |
1076 | ```reason 6 title="Intro Example" subtitle="Explicitly typed elements"
1077 | [@react.component]
1078 | let make = (~name) => {
1079 | let (count, setCount) = React.useState(() => 0);
1080 |
1081 |
1082 |
{React.string(name ++ " clicked " ++ string_of_int(count) ++ " times")}
1083 |
setCount(_ => count + 1)}>
1084 | {React.string("Click me")}
1085 |
1086 |
1087 | };
1088 | ```
1089 |
1090 | ```reason 1 title="Intro Example" subtitle="React decorator"
1091 | [@react.component]
1092 | let make = (~name) => {
1093 | let (count, setCount) = React.useState(() => 0);
1094 |
1095 |
1096 |
{React.string(name ++ " clicked " ++ string_of_int(count) ++ " times")}
1097 |
setCount(_ => count + 1)}>
1098 | {React.string("Click me")}
1099 |
1100 |
1101 | };
1102 | ```
1103 |
1104 |
1105 |
1106 |
1107 | Main differences between this and JS implementation:
1108 | * Named arguments for props
1109 | * Decorator attribute that tells ReasonReact you want to compile this into a
1110 | function that takes a JS object as props which is how React works
1111 | * React.string wrapper, conversion to an int
1112 |
1113 |
1114 | ---
1115 |
1116 | ## Main practical benefits of Reason + React
1117 |
1118 | * Types - ease of refactoring
1119 |
1120 | * Explicit handling of elements `React.string("hello")`
1121 |
1122 | * First Class Language Tools (immutability, switch statements)
1123 |
1124 |
1125 | 1. Refactoring - this comes from experience
1126 | 2. This is a nice sanity check
1127 | 3. Again, minimize application state, switches help for rendering different HTML
1128 |
1129 |
1130 | ---
1131 |
1132 |
1133 |
1134 |
1135 | ```reason title="Example - ReasonReactRouter"
1136 | // www.hello.com/book/10/edit?name=Jane#author
1137 |
1138 | let url = ReasonReactRouter.useUrl();
1139 | switch (url.path) {
1140 | | ["book", id, "edit"] => handleBookEdit(id)
1141 | | ["book", id] => getBook(id)
1142 | | ["book", id, _] => noSuchBookOperation()
1143 | | [] => showMainPage()
1144 | | ["shop"] | ["shop", "index"] => showShoppingPage()
1145 | | ["shop", ...rest] =>
1146 | /* e.g. "shop/cart/10", but let "cart/10" be handled by another function */
1147 | nestedMatch(rest)
1148 | | _ => showNotFoundPage()
1149 | };
1150 | }
1151 |
1152 | ```
1153 |
1154 | ```reason 1:5 title="Example - ReasonReactRouter"
1155 | // www.hello.com/book/10/edit?name=Jane#author
1156 |
1157 | let url = ReasonReactRouter.useUrl();
1158 | switch (url.path) {
1159 | | ["book", id, "edit"] => handleBookEdit(id)
1160 | | ["book", id] => getBook(id)
1161 | | ["book", id, _] => noSuchBookOperation()
1162 | | [] => showMainPage()
1163 | | ["shop"] | ["shop", "index"] => showShoppingPage()
1164 | | ["shop", ...rest] =>
1165 | /* e.g. "shop/cart/10", but let "cart/10" be handled by another function */
1166 | nestedMatch(rest)
1167 | | _ => showNotFoundPage()
1168 | };
1169 | }
1170 |
1171 | ```
1172 |
1173 |
1174 |
1175 |
1176 | The entire implementation is around 20 lines!
1177 |
1178 |
1179 | ---
1180 |
1181 |
1182 |
1183 | ```reason title="Another Example - Apollo"
1184 | open ApolloHooks
1185 |
1186 | module UserQuery = [%Graphql {|
1187 | query UserQuery {
1188 | currentUser {
1189 | name
1190 | }
1191 | }
1192 | |}];
1193 |
1194 | [@react.component]
1195 | let make = () => {
1196 | /* Both variant and records available */
1197 | let (simple, _full) = useQuery(UserQuery.definition);
1198 |
1199 |
1200 | {
1201 | switch(simple) {
1202 | | Loading =>
{React.string("Loading...")}
1203 | | Data(data) =>
1204 |
{React.string(data##currentUser##name)}
1205 | | NoData
1206 | | Error(_) =>
{React.string("Get off my lawn!")}
1207 | }
1208 | }
1209 |
1210 | }
1211 | ```
1212 |
1213 | ```reason 3:9 title="Another Example - Apollo" subtitle="Fully typed GraphQL queries!"
1214 | open ApolloHooks
1215 |
1216 | module UserQuery = [%Graphql {|
1217 | query UserQuery {
1218 | currentUser {
1219 | name
1220 | }
1221 | }
1222 | |}];
1223 |
1224 | [@react.component]
1225 | let make = () => {
1226 | /* Both variant and records available */
1227 | let (simple, _full) = useQuery(UserQuery.definition);
1228 |
1229 |
1230 | {
1231 | switch(simple) {
1232 | | Loading =>
{React.string("Loading...")}
1233 | | Data(data) =>
1234 |
{React.string(data##currentUser##name)}
1235 | | NoData
1236 | | Error(_) =>
{React.string("Get off my lawn!")}
1237 | }
1238 | }
1239 |
1240 | }
1241 | ```
1242 |
1243 | ```reason 11:30 title="Another Example - Apollo" subtitle="Render a different component easily"
1244 | open ApolloHooks
1245 |
1246 | module UserQuery = [%Graphql {|
1247 | query UserQuery {
1248 | currentUser {
1249 | name
1250 | }
1251 | }
1252 | |}];
1253 |
1254 | [@react.component]
1255 | let make = () => {
1256 | /* Both variant and records available */
1257 | let (simple, _full) = useQuery(UserQuery.definition);
1258 |
1259 |
1260 | {
1261 | switch(simple) {
1262 | | Loading =>
{React.string("Loading...")}
1263 | | Data(data) =>
1264 |
{React.string(data##currentUser##name)}
1265 | | NoData
1266 | | Error(_) =>
{React.string("Get off my lawn!")}
1267 | }
1268 | }
1269 |
1270 | }
1271 | ```
1272 |
1273 | ```reason 18:24 title="Another Example - Apollo" subtitle="Look at how elegant this is!"
1274 | open ApolloHooks
1275 |
1276 | module UserQuery = [%Graphql {|
1277 | query UserQuery {
1278 | currentUser {
1279 | name
1280 | }
1281 | }
1282 | |}];
1283 |
1284 | [@react.component]
1285 | let make = () => {
1286 | /* Both variant and records available */
1287 | let (simple, _full) = useQuery(UserQuery.definition);
1288 |
1289 |
1290 | {
1291 | switch(simple) {
1292 | | Loading =>
{React.string("Loading...")}
1293 | | Data(data) =>
1294 |
{React.string(data##currentUser##name)}
1295 | | NoData
1296 | | Error(_) =>
{React.string("Get off my lawn!")}
1297 | }
1298 | }
1299 |
1300 | }
1301 | ```
1302 |
1303 |
1304 |
1305 | ---
1306 |
1307 | ## So what did we (hopefully) learn?
1308 |
1309 |
1310 |
1311 | * Functional programming concepts
1312 |
1313 | * Deep dive into the language by writing a mini-compiler
1314 |
1315 | * Examples of how to use Reason + React to build powerful and extensible web apps
1316 |
1317 |
1318 |
1319 |
1320 | 1. Functions as first-class citizens, pure functions, immutability
1321 | 2. Lexer->Parser->CodeGenerator
1322 | 3. Mention ReasonReactNative
1323 |
1324 |
1325 | ---
1326 |
1327 | # Thank you + Q&A
1328 |
1329 | ---
1330 |
1331 | # Sources
1332 |
1333 | - https://thecodeboss.dev/2016/12/core-functional-programming-concepts/
1334 | - https://www.youtube.com/watch?v=5fG_lyNuEAw&list=PLDLhOs9UV9w9ms-kFEtR3-LMM3HU8QheA
1335 | - https://reasonml.github.io/
1336 |
--------------------------------------------------------------------------------
/gatsby-config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | siteMetadata: {
3 | title: "Hello yoo",
4 | },
5 | plugins: ["gatsby-theme-apollo"],
6 | };
7 |
--------------------------------------------------------------------------------
/lib/graphql/config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This is how you could configure a more complicated Apollo Client
3 | * with support for WebSockets, centralized error handling, and authentication
4 | */
5 |
6 | import fetch from "isomorphic-fetch";
7 | import { from, split } from "apollo-link";
8 | import { onError } from "apollo-link-error";
9 | import { createHttpLink } from "apollo-link-http";
10 | import { ApolloClient } from "apollo-client";
11 | // import { WebSocketLink } from "apollo-link-ws";
12 | import { setContext } from "apollo-link-context";
13 | import { getMainDefinition } from "apollo-utilities";
14 | import { InMemoryCache } from "apollo-cache-inmemory";
15 |
16 | /**
17 | * Apollo cache instance
18 | */
19 | const cache = new InMemoryCache();
20 |
21 | /**
22 | * HTTP Link
23 | */
24 | const httpLink = createHttpLink({
25 | uri: `https://luij2.sse.codesandbox.io`,
26 | fetch,
27 | });
28 |
29 | /**
30 | * WebSocket link for subscriptions
31 | */
32 | // const wsLink = new WebSocketLink({
33 | // uri: `ws://luij2.sse.codesandbox.io`,
34 | // options: {
35 | // reconnect: true,
36 | // },
37 | // });
38 |
39 | /**
40 | * Sets auth token on an individual request
41 | */
42 | const authorizationLink = setContext(
43 | async (request, previousContext) => {
44 | const headers = {
45 | authorization: `Bearer this_is_some_jwt_or_whatever`,
46 | };
47 |
48 | return { headers };
49 | }
50 | );
51 |
52 | /**
53 | * Single place to handle errors
54 | */
55 | const handleErrorLink = onError(
56 | ({ forward, graphQLErrors, operation, response }) => {
57 | /**
58 | * Do error handling here
59 | * Send to error handling service, trigger alert, etc.
60 | */
61 | return forward(operation);
62 | }
63 | );
64 |
65 | const remoteDataLink = split(
66 | ({ query }) => {
67 | const definition = getMainDefinition(query);
68 | return (
69 | definition.kind === "OperationDefinition" &&
70 | definition.operation === "subscription"
71 | );
72 | },
73 | // wsLink,
74 | httpLink
75 | );
76 |
77 | /**
78 | * Apollo Client instance
79 | */
80 | export const client = new ApolloClient({
81 | cache,
82 | link: from([
83 | authorizationLink,
84 | handleErrorLink,
85 | remoteDataLink,
86 | ]),
87 | });
88 |
--------------------------------------------------------------------------------
/now.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fp-react-compilers-reasonml",
3 | "alias": "fp-react-compilers-reasonml.now.sh",
4 | "builds": [
5 | {
6 | "src": "package.json",
7 | "use": "@now/static-build",
8 | "config": {
9 | "distDir": "public"
10 | }
11 | }
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "version": "1.0.0",
4 | "scripts": {
5 | "start": "mdx-deck deck.mdx",
6 | "build": "mdx-deck build deck.mdx",
7 | "test": "echo \"there are no tests\""
8 | },
9 | "devDependencies": {
10 | "code-surfer": "3.0.0-beta.1",
11 | "gatsby-source-graphql": "^2.1.20",
12 | "gatsby-theme-apollo": "^2.0.0",
13 | "mdx-deck": "^4.1.1"
14 | },
15 | "name": "fp-react-compilers-with-reasonml",
16 | "dependencies": {
17 | "@apollo/react-hooks": "^3.1.3",
18 | "@mdx-deck/themes": "^3.0.8",
19 | "apollo-boost": "^0.4.4",
20 | "apollo-client": "^2.6.4",
21 | "gatsby-theme-mdx-deck": "^4.1.0",
22 | "graphql-tag": "^2.10.1",
23 | "isomorphic-fetch": "^2.2.1",
24 | "lodash": "^4.17.15",
25 | "react-apollo": "^3.1.3",
26 | "react-inspector": "^4.0.0",
27 | "styled-components": "^4.4.0",
28 | "subscriptions-transport-ws": "^0.9.16"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/slides/Author/components/ImagesRow/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Image } from "mdx-deck";
3 | import { Row } from "../../../../components/ui/Common";
4 |
5 | const PreviousWork = props => {
6 | const { images, ...rest } = props;
7 | return (
8 |
9 | {images.map(src => {
10 | return (
11 |
21 | );
22 | })}
23 |
24 | );
25 | };
26 |
27 | export default PreviousWork;
28 |
--------------------------------------------------------------------------------
/slides/Author/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Row } from "../../components/ui/Common";
3 | import ImagesRow from "./components/ImagesRow";
4 |
5 | const Author = props => {
6 | return (
7 |
8 |
9 | Dylan Irlbeck ·
10 | Intern
11 | @
12 |
17 |
25 |
26 |
27 |
35 |
43 |
44 | );
45 | };
46 |
47 | export default Author;
48 |
--------------------------------------------------------------------------------
/slides/TableOfContents/components/ImagesRow/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Image } from "mdx-deck";
3 | import { Row } from "../../../../components/ui/Common";
4 |
5 | const PreviousWork = props => {
6 | const { images, ...rest } = props;
7 | return (
8 |
9 | {images.map(src => {
10 | return (
11 |
21 | );
22 | })}
23 |
24 | );
25 | };
26 |
27 | export default PreviousWork;
28 |
--------------------------------------------------------------------------------
/slides/TableOfContents/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const createStyleGetter = activeStep => step => ({
4 | opacity: activeStep === step ? 1 : 0.2,
5 | });
6 |
7 | const steps = [
8 | { title: "The Functional Approach" },
9 | { title: "Compiler Theory" },
10 | { title: "ReasonReact" },
11 | ];
12 |
13 | const TableOfContents = props => {
14 | const { activeStep } = props;
15 |
16 | const getStyle = createStyleGetter(activeStep);
17 |
18 | return (
19 |
20 | {steps.map((step, index) => {
21 | const { title } = step;
22 | return
{title} ;
23 | })}
24 |
25 | );
26 | };
27 |
28 | export default TableOfContents;
29 |
--------------------------------------------------------------------------------
/slides/WhyCompiler/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ImagesRow from "../Author/components/ImagesRow";
3 |
4 | const WhyCompiler = props => {
5 | return (
6 |
7 |
Why write a compiler?
8 |
9 | Show off the core features of Reason
10 |
11 |
12 | It's what Reason/OCaml is especially good at
13 |
14 |
22 |
23 | );
24 | };
25 |
26 | export default WhyCompiler;
27 |
--------------------------------------------------------------------------------
/src/gatsby-theme-apollo/client.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Apollo configurations can get MUCH more sophisticated than this
3 | * This is the most minimal setup you can use with SSR support
4 | */
5 |
6 | import fetch from "isomorphic-fetch";
7 | import ApolloClient from "apollo-boost";
8 |
9 | const client = new ApolloClient({
10 | fetch,
11 | uri: "https://luij2.sse.codesandbox.io",
12 | });
13 |
14 | export default client;
15 |
--------------------------------------------------------------------------------