Skip to content

Commit

Permalink
Merge pull request #5945 from roc-lang/homepage-example
Browse files Browse the repository at this point in the history
Add larger example to WIP homepage
  • Loading branch information
rtfeldman authored Oct 31, 2023
2 parents ad027e9 + 4b5b718 commit 849296a
Show file tree
Hide file tree
Showing 9 changed files with 556 additions and 95 deletions.
22 changes: 2 additions & 20 deletions crates/repl_ui/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,33 +13,15 @@ use roc_reporting::report::StyleCodes;

use crate::colors::GREEN;

// TODO add link to repl tutorial(does not yet exist).
// TODO add link to repl tutorial (does not yet exist).
pub const TIPS: &str = concatcp!(
"\nEnter an expression to evaluate, or a definition (like ",
BLUE,
"x = 1",
END_COL,
") to use later.\n\n",
if cfg!(target_family = "wasm") {
// In the web REPL, the :quit command doesn't make sense. Just close the browser tab!
// We use Shift-Enter for newlines because it's nicer than our workaround for Unix terminals (see below)
concatcp!(
BLUE,
" - ",
END_COL,
PINK,
"Shift-Enter",
END_COL,
" or ",
PINK,
"Ctrl-Enter",
END_COL,
" makes a newline\n",
BLUE,
" - ",
END_COL,
":help"
)
"" // In the web repl, we render tips in the UI around the repl instead of in the repl itself.
} else {
// We use ctrl-v + ctrl-j for newlines because on Unix, terminals cannot distinguish between Shift-Enter and Enter
concatcp!(
Expand Down
2 changes: 2 additions & 0 deletions www/generate_tutorial/src/input/tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -1565,6 +1565,8 @@ task =
Stdout.line "You just entered: \(text)"
```

## [Backpassing](#backpassing) {#backpassing}

This `<-` syntax is called _backpassing_. The `<-` is a way to define an anonymous function, just like `\ ... ->` is.

Here, we're using backpassing to define two anonymous functions. Here's one of them:
Expand Down
185 changes: 185 additions & 0 deletions www/wip_new_website/InteractiveExample.roc
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
interface InteractiveExample
exposes [view]
imports [pf.Html.{ pre, samp }, pf.Html.Attributes.{ class }]

Section : [Desc (List Token) Str, Indent, Outdent, Newline]
Token : [
Kw Str,
Ident Str,
Str Str,
Num Str,
Comment Str,
Literal Str,
ParensAround (List Token),
Lambda (List Str),
StrInterpolation Str Str Str,
]

view : Html.Node
view =
output =
# # Select anything here to see an explanation.
# main =
# Path.fromStr "url.txt"
# |> storeEmail
# |> Task.onErr handleErr
#
# storeEmail = \filename ->
# url <- File.readUtf8 filename |> Task.await
# { name, email } <- Http.get url Json.codec |> Task.await
#
# File.writeUtf8 (Path.fromStr "\(name).txt") email
#
# handleUrl = \err ->
# when err is
# HttpErr url _ -> Stderr.line "Error fetching URL \(url)"
# FileReadErr path _ -> Stderr.line "Error reading \(Path.display path)"
# FileWriteErr path _ -> Stderr.line "Error writing \(Path.display path)"
sectionsToStr [
Desc [Comment "<span class='desktop'>Click anything here to see an explanation.</span><span class='mobile'>Tap anything here to\n# see an explanation.</span>"] "<p><a href=\"/tutorial#comments\">Comments</a> in Roc begin with a <code>#</code> and go to the end of the line.</p>",
Newline,
Desc [Ident "main", Kw "="] "<p>This defines <code class=\"ident\">main</code>, which is where our program will begin.</p><p>In Roc, <a href=\"/tutorial#https://www.roc-lang.org/tutorial#naming-things\">all definitions are constant</a>, so writing <code class=\"ident\">main =</code> again in the same scope would give an error.</p>",
Indent,
Desc [Ident "Path.fromStr", Str "\"url.txt\""] "<p>This converts the string <code>\"url.txt\"</code> into a <code>Path</code> by passing it to <code>Path.fromStr</code>.</p><p>Function arguments are separated with whitespace. Parentheses are only needed in <a href=\"/tutorial#calling-functions\">nested function calls</a>.</p>",
Newline,
Desc [Kw "|>", Ident "storeEmail"] "<p>The <a href=\"/tutorial#the-pipe-operator\">pipe operator</a> (<code>|></code>) is syntax sugar for passing the previous value to the next function in the “pipeline.”</p><p>Here, we're taking the value returned by <code>Path.fromStr \"url.txt\"</code> and passing it to <code>storeEmail</code>.</p><p>The next <code>|></code> continues the pipeline.</p>",
Newline,
Desc [Kw "|>", Ident "Task.onErr", Ident "handleErr"] "<p>If the task returned by the previous step in the pipeline fails, pass its error to <code>handleErr</code>.</p><p>The pipeline essentially does this:</p><pre><code>val1 = Path.fromStr \"url.txt\"\nval2 = storeEmail val1\n\nTask.onErr val2 handleErr</code></pre><p>It creates a <code>Path</code> from a string, stores an email based on that path, and then does error handling.</p>",
Outdent,
Newline,
Desc [Ident "storeEmail", Kw "=", Lambda ["filename"]] "<p>This <a href=\"/tutorial#defining-functions\">defines a function</a> named <code>storeEmail</code>.</p><p>In Roc, functions are ordinary values, so we assign names to them using <code>=</code> like with any other value.</p><p>The <code>\\arg1, arg2 -&gt;</code> syntax begins a function, and the part after <code>-&gt;</code> is the function's body.</p>",
Indent,
Desc [Ident "url", Kw "<-", Ident "File.readUtf8", Ident "filename", Kw "|>", Ident "Task.await"] "<p>This reads the contents of the file (as a <a href=\"https://en.wikipedia.org/wiki/UTF-8\">UTF-8</a> string) into <code>url</code>.</p><p>The <code>&lt;-</code> does <a href=\"/tutorial#backpassing\">backpassing</a>, which is syntax sugar for defining a function. This whole line desugars to:</p><pre><code>Task.await\n (File.readUtf8 filename)\n \\url -&gt;</code></pre><p>The lines after this one form the body of the <code>\\url -&gt;</code> <a href=\"https://en.wikipedia.org/wiki/Callback_(computer_programming)\">callback</a>, which runs if the file read succeeds.</p>",
Newline,
Desc [Ident "user", Kw "<-", Ident "Http.get", Ident "url", Ident "Json.codec", Kw "|>", Ident "Task.await"] "<p>This fetches the contents of the URL and decodes them as <a href=\"https://www.json.org\">JSON</a>.</p><p>If the shape of the JSON isn’t compatible with the type of <code>user</code> (based on type inference), this will give a decoding error immediately.</p><p>As with all the other lines ending in <code>|> Task.await</code>, if there’s an error, nothing else in <code>storeEmail</code> will be run, and <code>handleErr</code> will end up handling the error.</p>",
Newline,
Desc [Ident "dest", Kw "=", StrInterpolation "\"" "user.name" ".txt\""] "<p>The <code>\\(user.name)</code> in this string literal will be replaced with the value in <code>name</code>. This is <a href=\"/tutorial#string-interpolation\">string interpolation</a>.</p><p>Note that this line doesn't end with <code>|> Task.await</code>. Earlier lines needed that because they were I/O <a href=\"/tutorial#tasks\">tasks</a>, but this is a plain old <a href=\"/tutorial#defs\">definition</a>, so there's no task to await.</p>",
Newline,
Desc [Literal "_"] "<p>In Roc, if you don’t want to bother naming something, you can always choose the name <code>_</code>.</p><p>You can name as many things as you like <code>_</code>, but you can never reference anything named <code>_</code>.</p><p>So it’s only useful for when you don’t want to choose a name.</p>",
Desc [Kw "<-", Ident "File.writeUtf8", ParensAround [Ident "Path.fromStr dest"], Ident "user.email", Kw "|>", Ident "Task.await"] "<p>This writes the <code>user.email</code> string to the file encoded as <a href=\"https://en.wikipedia.org/wiki/UTF-8\">UTF-8</a>.</p><p>The parentheses here show where the nested call to <code>Path.fromStr</code> begins and ends.</p>",
Newline,
Desc [Ident "Stdout.line", StrInterpolation "\"Wrote email to " "dest" "\""] "<p>This prints what we did to <a href=\"https://en.wikipedia.org/wiki/Standard_streams#Standard_output_(stdout)\">stdout</a>.</p><p>Note that this line doesn't end with <code>|> Task.await</code>. That’s because, although <code>Stdout.line</code> returns a <a href=\"/tutorial#tasks\">task</a>, we don’t need to await it because nothing happens after it.</p>",
Outdent,
Newline,
Desc [Ident "handleErr", Kw "=", Lambda ["err"]] "<p>Like <code>storeEmail</code>, <code>handleErr</code> is also a function.</p><p>Although type annotations are optional everywhere in Roc—because the language has 100% type inference—you could add type annotations to <code>main</code>, <code>storeEmail</code>, and <code>handleErr</code> if you wanted to.</p>",
Indent,
Desc [Kw "when", Ident "err", Kw "is"] "<p>TODO when</p>",
Indent,
Desc [Literal "HttpErr", Ident "path", Kw "_", Kw "->"] "<p>TODO</p>",
Desc [Ident "Stderr.line", StrInterpolation "\"Error fetching URL " "url" "\""] "<p>TODO</p>",
Newline,
Desc [Literal "FileReadErr", Ident "path", Kw "_", Kw "->"] "<p>TODO</p>",
Desc [Ident "Stderr.line", StrInterpolation "\"Error reading from " "Path.display path" "\""] "<p>TODO</p>",
Newline,
Desc [Literal "FileWriteErr", Ident "path", Kw "_", Kw "->"] "<p>TODO</p>",
Desc [Ident "Stderr.line", StrInterpolation "\"Error writing to " "Path.display path" "\""] "<p>TODO</p>",
]

pre [class "interactive-example"] [
samp [] [
Html.text output,
],
]

tokensToStr : List Token -> Str
tokensToStr = \tokens ->
List.walk tokens "" \buf, token ->
bufWithSpace =
if Str.isEmpty buf || token == Literal "," then
buf
else
Str.concat buf " "

when token is
ParensAround wrapped ->
# Don't put spaces after opening parens or before closing parens
bufWithSpace
|> Str.concat "<span class=\"kw\">(</span>"
|> Str.concat (tokensToStr wrapped)
|> Str.concat "<span class=\"kw\">)</span>"

Lambda args ->
# Don't put spaces after opening parens or before closing parens
argsWithCommas =
args
|> List.map \ident -> "<span class=\"ident\">\(ident)</span>"
|> Str.joinWith "<span class=\"literal\">,</span> "

bufWithSpace
|> Str.concat "<span class=\"kw\">\\</span>"
|> Str.concat argsWithCommas
|> Str.concat "<span class=\"kw\"> -></span>"

Kw str ->
Str.concat bufWithSpace "<span class=\"kw\">\(str)</span>"

Num str | Str str | Literal str -> # We may render these differently in the future
Str.concat bufWithSpace "<span class=\"literal\">\(str)</span>"

Comment str ->
Str.concat bufWithSpace "<span class=\"comment\"># \(str)</span>"

Ident str ->
Str.concat bufWithSpace (identToHtml str)

StrInterpolation before interp after ->
bufWithSpace
|> Str.concat (if Str.isEmpty before then "" else "<span class=\"literal\">\(before)</span>")
|> Str.concat "<span class=\"kw\">\\(</span>\(identToHtml interp)<span class=\"kw\">)</span>"
|> Str.concat (if Str.isEmpty after then "" else "<span class=\"literal\">\(after)</span>")

identToHtml : Str -> Str
identToHtml = \str ->
List.walk (Str.split str ".") "" \accum, ident ->
identHtml = "<span class=\"ident\">\(ident)</span>"

if Str.isEmpty accum then
identHtml
else
"\(accum)<span class=\"kw\">.</span>\(identHtml)"

sectionsToStr : List Section -> Str
sectionsToStr = \sections ->
answer = List.walk sections { buf: "", count: 0, indent: 0 } \{ buf, count, indent }, section ->
bufWithSpace =
if Str.isEmpty buf then
buf
else if buf |> Str.endsWith "\n" then
Str.concat buf (Str.repeat " " indent)
else
Str.concat buf " "

(afterSpaces, nextCount) =
when section is
Newline | Indent | Outdent ->
# Indent and outdent changes happen on the next iteration,
# so we only need a newline for them here.
(Str.concat buf "\n", count)

Desc tokens str ->
html = radio count (tokensToStr tokens) str

(Str.concat bufWithSpace html, count + 1)

nextIndent =
when section is
Indent -> indent + 4
Outdent -> indent - 4
Newline | Desc _ _ -> indent

{
buf: afterSpaces,
count: nextCount,
indent: nextIndent,
}

answer.buf

radio : U16, Str, Str -> Str
radio = \index, labelHtml, descHtml ->
# The first radio button should always be checked, and none of the others should be.
checkedHtml = if index == 0 then " checked" else ""

"""
<input class="interactive-radio" type="radio" name="r" id="r\(Num.toStr index)" autocomplete=\"off\"\(checkedHtml)><label for="r\(Num.toStr index)" title="Tap to learn about this syntax">\(labelHtml)</label><div class="interactive-desc">\(descHtml)</div>
"""
6 changes: 6 additions & 0 deletions www/wip_new_website/build-dev-local.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@

# NOTE run `bash www/build.sh` to cache local copy of fonts, and repl assets etc

## Get the directory of the currently executing script
DIR="$(dirname "$0")"

# Change to that directory
cd "$DIR" || exit

rm -rf dist/
cp -r ../build dist/
mkdir -p dist/wip
Expand Down
76 changes: 55 additions & 21 deletions www/wip_new_website/content/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,44 +27,78 @@
<section class="home-goals-container">
<div class="home-goals-column">
<div class="home-goals-content">
<h3 class="home-goals-title">Fast</h3>
<p class="home-goals-description">Roc code is designed to build fast and run fast. It compiles to machine code or <a href="https://webassembly.org/">WebAssembly</a>.</p>
<h3 class="home-goals-title"><a href="/fast">Fast</a></h3>
<p class="home-goals-description">Roc code is designed to build fast and run fast. <span class="nobreak-on-mobile">It compiles to machine code or WebAssembly.</span></p>
<p class="home-goals-learn-more"><a href="/fast">What does <i>fast</i> mean here?</a></p>
</div>
</div>
<div class="home-goals-column">
<div class="home-goals-content">
<h3 class="home-goals-title">Friendly</h3>
<p class="home-goals-description">Roc's syntax, semantics, and toolset are designed to feel user-friendly and helpful.</p>
<h3 class="home-goals-title"><a href="/friendly">Friendly</a></h3>
<p class="home-goals-description">Rocs syntax, semantics, and included toolset <span class="nobreak-on-mobile">all prioritize user-friendliness.</span></p>
<p class="home-goals-learn-more"><a href="/friendly">What does <i>friendly</i> mean here?</a></p>
</div>
</div>
<div class="home-goals-column">
<div class="home-goals-content">
<h3 class="home-goals-title">Functional</h3>
<h3 class="home-goals-title"><a href="/functional">Functional</a></h3>
<p class="home-goals-description">
Roc has a small number of simple language primitives. It's a single-paradigm <a href="https://en.wikipedia.org/wiki/Functional_programming">functional</a> language.</p>
Roc has a small number of simple language primitives. <span class="nobreak-on-mobile">It’s a single-paradigm functional language.</span></p>
<p class="home-goals-learn-more"><a href="/design_goals.html#functional">What does <i>functional</i> mean here?</a></p>
</div>
</div>
</section>

## Try Roc

<div id="repl">
<code class="history">
<div id="help-text"></div>
<div id="history-text"><div id="loading-message">Loading REPL WebAssembly module…please wait!</div></div>
</code>
<section id="source-input-wrapper">
<textarea rows="5" id="source-input" placeholder="You can enter Roc code here once the REPL loads!"
disabled></textarea>
<section id="homepage-repl-container">
<div id="repl-description" role="presentation">
<p>You can try out Roc using this read-eval-print loop (<a href="https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop">REPL</a>), which is running in your browser in <a href="https://webassembly.org">WebAssembly</a>.</p>
<p><code>Shift-Enter</code> adds a newline.</p>
<p><span id="repl-arrow">←</span> Try entering <code>0.1 + 0.2</code></p>
</div>
<div id="repl" role="presentation">
<code class="history">
<div id="help-text"></div>
<div id="history-text"><div id="loading-message">Loading REPL WebAssembly module…please wait!</div></div>
</code>
<section id="source-input-wrapper">
<textarea rows="5" id="source-input" placeholder="You can enter Roc code here once the REPL loads!"
onfocus="document.getElementById('repl-arrow').style.display='none';"></textarea>
</section>
</div>
<script type="module" src="/wip/repl.js"></script>
</section>
</div>
<script type="module" src="/wip/repl.js"></script>
</div>

## Examples
## Use Cases

Roc is a very young language (it doesn’t even have a numbered release yet, just nightly builds!) but it can already be used for several things if you’re up for being an early adopter—with all the bugs and missing features which come with that territory.

Currently these use cases are the best-supported:

### Command-Line Interfaces (CLIs)

### Web Servers

### Embedding

Calling Roc functions from another language

### Others

You can create your own! Learn about **platforms and applications**...

## Larger Example

Here’s a larger example that shows a few different aspects of Roc:
* File I/O and HTTP requests
* Pattern matching for error handling
* JSON deserialization via type inference
* Common syntax sugar: string interpolation, pipelines, and backpassing

The [tutorial](/tutorial) introduces these gradually and in more depth, but this gives you a brief overview.

<!-- ## More Examples
We have developed a number of smaller code [examples](https://github.com/roc-lang/examples) which demonstrate how to use Roc. These cover a range of topics from basic syntax to more advanced features such as random number generation and using the popular `Task` feature.
Expand All @@ -85,9 +119,9 @@ TODO provide explanation of platform/application abstraction versus libraries as
If you'd like to learn more about Roc check out one of these videos:
* [Roc at Handmade Seattle](https://media.handmade-seattle.com/roc-lang) - November 12, 2021 (very low-level explanation of how Roc's compiler makes programs run fast)
* [Outperforming Imperative with Pure Functional Languages](https://youtu.be/vzfy4EKwG_Y) - October 1, 2021 (about Roc's runtime performance and optimizer)
* [Roc at Handmade Seattle](https://media.handmade-seattle.com/roc-lang) - November 12, 2021 (very low-level explanation of how Rocs compiler makes programs run fast)
* [Outperforming Imperative with Pure Functional Languages](https://youtu.be/vzfy4EKwG_Y) - October 1, 2021 (about Rocs runtime performance and optimizer)
* [A taste of Roc](https://youtu.be/6qzWm_eoUXM) - September 23, 2021 (syntax, application examples)
* [Roc at the Philly ETE conference](https://youtu.be/cpQwtwVKAfU?t=75) - May 6, 2021 (platforms and applications)
* [Roc on Zig Showtime](https://youtu.be/FMyyYdFSOHA) - April 24, 2021 (making a platform)
* [Roc at the Berlin FP Meetup](https://youtu.be/ZnYa99QoznE?t=4790) - September 1, 2020 (overall vision for the language)
* [Roc at the Berlin FP Meetup](https://youtu.be/ZnYa99QoznE?t=4790) - September 1, 2020 (overall vision for the language) -->
Loading

0 comments on commit 849296a

Please sign in to comment.