Skip to content

Commit

Permalink
Merge pull request #29 from melange-re/dune-syntax
Browse files Browse the repository at this point in the history
Merge Exercise and Solutions sections
  • Loading branch information
feihong authored Mar 13, 2024
2 parents d7b959c + cdda377 commit 805d64b
Show file tree
Hide file tree
Showing 21 changed files with 3,483 additions and 345 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: 18
node-version: 20
cache: npm
- uses: ocaml/setup-ocaml@v2
with:
Expand Down
28 changes: 17 additions & 11 deletions docs/.vitepress/config.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import { readFileSync } from "fs"
import { readFileSync } from 'fs'
import { defineConfig } from 'vitepress'
import markdownItFootnote from 'markdown-it-footnote'

// From https://github.com/ocamllabs/vscode-ocaml-platform/blob/master/syntaxes/reason.json
const reasonGrammar = JSON.parse(readFileSync("./reason.json"))

// https://vitepress.dev/reference/site-config
export default defineConfig({
title: "Melange for React Devs",
description: "A project-based, guided introduction to Melange and its ecosystem for React developers",
title: 'Melange for React Devs',
description: 'A project-based, guided introduction to Melange and its ecosystem for React developers',
base: '/',
lastUpdated: true,
themeConfig: {
Expand Down Expand Up @@ -53,12 +50,21 @@ export default defineConfig({
md.use(markdownItFootnote)
},
languages: [
// Source: https://github.com/ocamllabs/vscode-ocaml-platform/blob/master/syntaxes/reason.json
{
id: 'reason',
scopeName: 'source.reason',
displayName: 'Reason',
grammar: JSON.parse(readFileSync('./syntaxes/reason.json')),
aliases: ['re', 'rei'],
},
// Source: https://github.com/ocamllabs/vscode-ocaml-platform/blob/master/syntaxes/dune.json
// with a few additions to support melange.emit
{
id: "reason",
scopeName: "source.reason",
displayName: "Reason",
grammar: reasonGrammar,
aliases: ["re", "rei"],
id: 'dune',
scopeName: 'source.dune',
displayName: 'Dune',
grammar: JSON.parse(readFileSync('./syntaxes/dune.json')),
},
],
},
Expand Down
5 changes: 5 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,8 @@ rel="noreferrer noopener">http://localhost:5173/</a>
```bash
make serve
```

## Syntax files

Syntax highlighting files were copied from
https://github.com/ocamllabs/vscode-ocaml-platform/blob/master/syntaxes
16 changes: 8 additions & 8 deletions docs/better-burgers/Item.re
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ module ItemV2 = {
{js|🧀×|js} ++ string_of_int(burger.cheese),
|]
|> Js.Array.filter(~f=str => str != "")
|> Js.Array.join(~sep=", "),
|> Js.Array.join(~sep=","),
);
// #endregion to-emoji

Expand All @@ -75,7 +75,7 @@ module ItemV2 = {
{js|🧀×|js} ++ string_of_int(cheese),
|]
|> Js.Array.filter(~f=str => str != "")
|> Js.Array.join(~sep=", "),
|> Js.Array.join(~sep=","),
);
};
// #endregion destructure-burger
Expand All @@ -94,7 +94,7 @@ module ItemV2 = {
{js|🧀×|js} ++ string_of_int(cheese),
|]
|> Js.Array.filter(~f=str => str != "")
|> Js.Array.join(~sep=", "),
|> Js.Array.join(~sep=","),
);
// #endregion destructure-burger-branch

Expand All @@ -115,7 +115,7 @@ module ItemV2 = {
multiple({js|🧀|js}, cheese),
|]
|> Js.Array.filter(~f=str => str != "")
|> Js.Array.join(~sep=", "),
|> Js.Array.join(~sep=","),
);
};
// #endregion multiple
Expand All @@ -142,7 +142,7 @@ module ItemV3 = {
multiple({js|🧀|js}, cheese),
|]
|> Js.Array.filter(~f=str => str != "")
|> Js.Array.join(~sep=", "),
|> Js.Array.join(~sep=","),
);
};

Expand Down Expand Up @@ -233,7 +233,7 @@ module ItemV3 = {
multiple({js|🧀|js}, cheese),
|]
|> Js.Array.filter(~f=str => str != "")
|> Js.Array.join(~sep=", "),
|> Js.Array.join(~sep=","),
);
};
// #endregion ternary
Expand All @@ -256,7 +256,7 @@ module ItemV3 = {
multiple({js|🧀|js}, cheese),
|]
|> Js.Array.filter(~f=str => str != "")
|> Js.Array.join(~sep=", "),
|> Js.Array.join(~sep=","),
)
};
};
Expand All @@ -280,7 +280,7 @@ module ItemV3 = {
multiple({js|🧀|js}, cheese),
|]
|> Js.Array.filter(~f=str => str != "")
|> Js.Array.join(~sep=", "),
|> Js.Array.join(~sep=","),
)
};
};
Expand Down
106 changes: 60 additions & 46 deletions docs/better-burgers/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ let toPrice = ({onions, cheese, lettuce}) => {
You get an error from Melange:

```
File "src/better-burgers/Item.re", line 24, characters 34-41:
File "src/order-confirmation/Item.re", line 24, characters 34-41:
24 | let toPrice = ({onions, cheese, lettuce}) => {
^^^^^^^
Error (warning 27 [unused-var-strict]): unused variable lettuce.
Expand All @@ -148,7 +148,7 @@ let toPrice = ({onions, cheese}) => {
However, this results in a different error:

```
File "src/better-burgers/Item.re", line 24, characters 17-33:
File "src/order-confirmation/Item.re", line 24, characters 17-33:
24 | let toPrice = ({onions, cheese}) => {
^^^^^^^^^^^^^^^^
Error (warning 9 [missing-record-field-pattern]): the following labels are not bound in this record pattern:
Expand Down Expand Up @@ -178,7 +178,7 @@ fields of a record type.
## Pattern matching records

Right now, if a customer doesn't add any toppings to a burger,
`Item.Burger.toPrice` will return `🍔{🧅×0, 🧀×0}`. It would be better if it
`Item.Burger.toPrice` will return `🍔{🧅×0,🧀×0}`. It would be better if it
just returned `🍔` to make it clear that it's a burger without any
embellishments. One way to handle this is to use a ternary expression:

Expand Down Expand Up @@ -216,44 +216,6 @@ the branches of the switch expression. Now `Item.Burger.toEmoji` gets the name
Magnifique! The order confirmation widget now supports burgers with different
toppings. In the next chapter, we'll start writing tests for our code.

## Exercises

<b>1.</b> Inside `Item.re`, create another submodule for sandwich-related types
and functions.

<b>2.</b> Add `tomatoes: bool` and `bacon: int` fields to `Item.Burger.t`. Let’s
say that adding tomatoes costs $0.05 and each piece of bacon[^1] costs $0.5.

<b>3.</b> Make `Item.Burger.toPrice` function more readable by writing a helper
function that calculates the cost of a topping by multiplying its price with its
quantity.

```reason
let toPrice = ({onions, cheese, tomato, bacon, lettuce: _}) => {
let toppingCost = /* write me */;
15. // base cost
+. toppingCost(onions, 0.2)
+. toppingCost(cheese, 0.1)
/* some stuff involving tomato and bacon */;
};
```

<b>4.</b> Right now, the `Item.Burger.toEmoji` function shows more emojis than
absolutely necessary. Refactor the `multiple` inner function in `Burger.toEmoji`
so that it exhibits the following behavior:

| `item` | `Item.Burger.toEmoji(item)` |
| ------ | --------------- |
| `Burger({lettuce: true, onions: 1, cheese: 1})` | `🍔{🥬,🧅,🧀}` |
| `Burger({lettuce: true, onions: 0, cheese: 0})` | `🍔{🥬}` |

::: details Hint

Use a switch expression.

:::

## Overview

- Record types are like `Js.t` objects but their fields must be explicitly
Expand Down Expand Up @@ -287,16 +249,28 @@ Use a switch expression.
- Try not to pattern match on tuples of more than 2 elements because it tends to
be hard to read

## Solutions
## Exercises

<b>1.</b> The `Item.Sandwich` submodule should look something like this:
<b>1.</b> Inside `Item.re`, create another submodule for sandwich-related types
and functions.

::: details Solution

The `Item.Sandwich` submodule should look something like this:

<<< Item.re#sandwich-module

You also need to refactor `Item.toPrice` and `Item.toEmoji` functions to use the
new functions in `Item.Sandwich`.

<b>2.</b> After adding `tomatoes` and `bacon` fields to `Item.Burger.t`, the
:::

<b>2.</b> Add `tomatoes: bool` and `bacon: int` fields to `Item.Burger.t`. Let’s
say that adding tomatoes costs $0.05 and each piece of bacon[^1] costs $0.5.

::: details Solution

After adding `tomatoes` and `bacon` fields to `Item.Burger.t`, the
changes to `Item.Burger.toEmoji` are fairly mechanical, so let's focus on
`Item.Burger.toPrice`:

Expand All @@ -305,12 +279,50 @@ changes to `Item.Burger.toEmoji` are fairly mechanical, so let's focus on
Note that we keep `lettuce: _` at the end of the pattern match since the value
of `lettuce` isn't ever used.

<b>3.</b> The `toppingCost` helper function inside `Burger.toPrice` should look
:::

<b>3.</b> Make `Item.Burger.toPrice` function more readable by writing a helper
function that calculates the cost of a topping by multiplying its price with its
quantity.

```reason
let toPrice = ({onions, cheese, tomato, bacon, lettuce: _}) => {
let toppingCost = /* write me */;
15. // base cost
+. toppingCost(onions, 0.2)
+. toppingCost(cheese, 0.1)
/* some stuff involving tomato and bacon */;
};
```

::: details Solution

After adding the `toppingCost` helper function, `Burger.toPrice` should look
something like this:

<<< Item.re#to-price-topping-cost

<b>4.</b> After refactoring the `multiple` helper function inside
:::

<b>4.</b> Right now, the `Item.Burger.toEmoji` function shows more emojis than
absolutely necessary. Refactor the `multiple` inner function in `Burger.toEmoji`
so that it exhibits the following behavior:

| `item` | `Item.Burger.toEmoji(item)` |
| ------ | --------------- |
| `Burger({lettuce: true, onions: 1, cheese: 1})` | `🍔{🥬,🧅,🧀}` |
| `Burger({lettuce: true, onions: 0, cheese: 0})` | `🍔{🥬}` |

::: details Hint

Use a switch expression.

:::

::: details Solution

After refactoring the `multiple` helper function inside
`Item.Burger.toEmoji` to avoid showing unnecessary emojis, it should look
something like this:

Expand All @@ -329,6 +341,8 @@ the version that uses a switch expression:

<<< Item.re#to-emoji-multiple-fun-annotated

:::

-----

View [source
Expand Down
3 changes: 1 addition & 2 deletions docs/better-sandwiches/Item.re
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,7 @@ module ItemAddTurducken = {
| Hotdog => {js|🌭|js}
| Sandwich(sandwich) =>
Printf.sprintf(
"%s(%s)",
{js|🥪|js},
{js|🥪(%s)|js},
switch (sandwich) {
| Portabello => {js|🍄|js}
| Ham => {js|🐷|js}
Expand Down
Loading

0 comments on commit 805d64b

Please sign in to comment.