Skip to content

Commit

Permalink
Deploy hds/lunch.rs to hds/lunch.rs:gh-pages
Browse files Browse the repository at this point in the history
  • Loading branch information
GitHub Actions committed Oct 25, 2024
0 parents commit b7e8e06
Show file tree
Hide file tree
Showing 34 changed files with 2,550 additions and 0 deletions.
Empty file added .nojekyll
Empty file.
3 changes: 3 additions & 0 deletions 404.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<!doctype html>
<title>404 Not Found</title>
<h1>404 Not Found</h1>
1 change: 1 addition & 0 deletions CNAME
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
lunch.rs
76 changes: 76 additions & 0 deletions about/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<!DOCTYPE html>
<html lang="en_GB">

<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">

<title>About Rust for Lunch - Rust for Lunch</title>

<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
<link rel="alternate" type="application/rss+xml" title="Rust for Lunch Meet-ups" href="/rss.xml" />
<link rel="icon" href="/favicon.ico" type="image/x-icon">

<link rel="stylesheet" href="/css/lunch.css">
</head>

<body>
<div class="masthead" role="navigation">
<div class="container">
<nav>
<a class="nav-item" href="/">lunch</a>
<a class="nav-item" href="/about/">about</a>
</nav>
</div>
</div>
<div class="container" role="main">


<div class="post">
<h1><a href="https:&#x2F;&#x2F;lunch.rs&#x2F;about&#x2F;">About Rust for Lunch</a></h1>

<div><p>Rust for Lunch is an online-only Rust meet scheduled to fit into a lunch break.</p>
<p>The main part of each meet-up will be short enough to fit into a 1 hour lunch
slot and the time is chosen to be a feasible lunch time.</p>
<p>The first meet-up will be scheduled around European and African midday and
early afternoon.</p>
<h2 id="code-of-conduct">Code of Conduct</h2>
<p>The Rust for Lunch project adheres to the <a href="https://github.com/rust-lang/rust/blob/master/CODE_OF_CONDUCT.md">Rust Code of Conduct</a>. This
describes the <em>minimum</em> behavior expected from all contributors and as well as
attendees.</p>
<p>Instances of violations of the Code of Conduct can be reported by contacting
the Hayden at his personal email address
<a href="mailto:[email protected]">[email protected]</a>.</p>
<h2 id="disclaimer">Disclaimer</h2>
<p>Rust for Lunch is not associated with or endorsed by neither the
<a href="https://www.rust-lang.org/">Rust Project</a> nor the
<a href="https://foundation.rust-lang.org/">Rust Foundation</a>.</p>
<h2 id="social">Social</h2>
<p>We have social spaces where meet-up details are announced. You're also welcome
to join and discuss whatever topic interests you, especially Rust of course.</p>
<ul>
<li>Matrix room: <a href="https://matrix.to/#/#rust-for-lunch:matrix.org">#rust-for-lunch:matrix.org</a></li>
<li>Discord server: <a href="https://discord.gg/DbcWXPR5aq">discord.gg/DbcWXPR5aq</a></li>
</ul>
<h2 id="contributing">Contributing</h2>
<p>We would love to have more people involved in the Rust for Lunch meetup.</p>
<p>Please get in touch with Hayden if you would like to speak at a meet-up.</p>
<p>If you would like to contribute in another way, either get in touch or
open an issue on the
<a href="https://github.com/hds/lunch.rs">lunch.rs GitHub project</a>.</p>
<h2 id="contact">Contact</h2>
<p>You can get in touch with Hayden at his
<a href="mailto:[email protected]">personal email address</a>. See the <a href="https://hegdenu.net/about/#contact">hegdenu.net about page</a> for more ways.</p>
</div>
</div>


<div class="footer" role="footer">
<div class="separator"></div>
<p><span class="current-year">2023</span>, Hayden Stainsby.</p>
</div>
</div>

</body>

</html>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added content/2024-07-09/pic1.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added content/2024-07-09/pic2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
224 changes: 224 additions & 0 deletions content/2024-07-09/slides.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
---
title: Parsing command line options with Category Theory
author: Michael Baykov
---

# Rust types and functions

Two of the main building blocks in Rust are functions and values. Values come in different
types, functions let you to convert between them. Not all functions are the same though, some
depend on the inputs only and only produce some resulting value, some - do something else.

Functions of first type are called pure and they are great - they are easy to work with, easy
to test, easy to reason about.

```rust
let a = 3;

fn inc(v: usize) -> usize [
v + 1
}

let b = inc(a)
```

<!-- end_slide -->

# Pure functions, side effects

Functions compose - that is given two functions: from A to B and from B to C you can make a
function from A to C. We can think of a function as always taking a value in and always
producing a value. If it takes none - it's `()`, if it takes multiple values - it's a tuple.
Same with results - use tuples or `()`.

![](pic1.jpg)

<!-- end_slide -->

# Parsers

Then there's parsers. A typical shape of a parser looks like this

```rust
Fn(I) -> Result<(I, T), Error>
```

```rust
Fn(&mut I) -> Result<T, Error>
```

First doesn't compose well, second - contains side effects. let's look into ways to manage
those effects.

<!-- end_slide -->

# Modeling side effects

We start with the easiest possible side effects - a computation that can fail. In Rust you'd
use `Option` or `Result` for that.

```rust
fn bad(i: Option<u8>) -> Option<u8> {
match i {
None => Some(42),
Some(42) => Some(1),
_ => None
}
}
```

```rust
fn good(i: Option<u8>) -> Option<u8> {
match i {
None => None,
Some(i) => Some(i + 3),
}
}
```

They are both valid functions, but for "bad" case pure computation and effects are mixed
together, while in "good" one - effects are separate

<!-- end_slide -->

# Functor

Second function represents something called a `Functor`

Like pure functions Functors are also great. If you have something that obeys Functor laws -
you can plug any pure computation inside and it will just work, easy to test too. Rust comes
with a bunch of Functors: `Option::map`, `Result::map`. `Iterator<Item = A>::map` is also a
functor

Functor must preserve identity morphisms and compositions of morphisms:

`a.map(id) == a`, `a.map(f).map(g) == a.map(g . f)`. But that's just a complicated way of
saying - "don't throw away information and maintain the computation shape". In the previous
example "bad" throws away and invents stuff, "good" does great.

In general I'm going to use a notation `F<A>` for types and `map f` for arrows, where `F` is a
type or a trait concrete for this Functor, and `A` is some variable Functor knows nothing about
so it can only apply morphisms like `Fn(A) -> B` and nothing else

![](pic2.png)

<!-- end_slide -->

# Applicative

Before I said we'll assume functions take a single argument and for multiple - we make a tuple.

Consider two values `A` and `B` and a function `f: Fn((A, B)) -> C` and think how corresponding
Functor might look like. A tuple is `(F<A>, F<B>)` and there's no corresponding pure
functions. Not good. We need to introduce one more bit of abstraction.

**Suppose `F` is a functor, then for two computations**
**`F<A>` and `F<B>` we can make `Fn((F<A>, F<B>)) -> F<(A, B)>`**

For `Option` this is `Option::zip`, for `Result`.... It can also be `zip`, as long as errors
agree, but for some reason it's missing. There's also `Iterator::zip` that does it for two
iterators. Same idea

[functor like category, `(A, B) -> C` on the left, `(F<A>, F<B>) -> F<(A, B)> -> F<C>` on the right]

<!-- end_slide -->

# Alternative

Getting somewhere. We know that the parser might fail so we must introduce some notion of
failure. For `Option` this is `None`, for `Result` this is `Err`.

We can add two functions to our abstract interface

```
pure: Fn(T) -> F<T>
fail: Fn(E) -> F<T>
```

And now that we know that some computation can fail - we want something to be able to try
several of them

```rust
alt: Fn(F<T>, F<T>) -> F<T>
```

In Rust this is `Option::or` and `Result::or` (and their variants)

<!-- end_slide -->

# Making a simple parser

Now that we have all the basic methods we can look into making something with the parser

```rust
map: Fn(F<A>, Fn(A) -> B) -> F<B>
zip: Fn(F<A>, F<B>) -> F<(A, B)>
pure: Fn(A) -> F<A>
fail: Fn(E) -> F<A>
alt: Fn(F<A>, F<A>) -> F<A>
```

It doesn't matter which version of the parser we begin with -

```rust
Fn(I) -> Result<(I, T), Error> = ...
Fn(&mut I) -> Result<T, Error> = ...
```

```rust
struct Parser<T>(Box<Fn(&mut Args)> -> Result<T, Error>);
```

```rust
trait Parser<A> {
fn map(self, impl Fn(A) -> B) -> impl Parser<B> {..}
fn zip(self, other: impl Parser<B>) -> impl Parser<(A, B)> {..}
...
}
```

<!-- end_slide -->

# Methods we can implement

```rust
trait Parser<A> {
// Required to compose

fn map(self, f: impl Fn(F<A>, Fn(A) -> B) -> impl Parser<B> {..}
fn zip(self, other: impl Parser<B>) -> impl Parser<(A, B)> {..}
fn pure(val: A) -> impl Parser<A> {..}
fn fail(message: &'static str) -> impl Parser<A> {..}
fn alt(self, other: impl Parser<A>) -> impl Parser<A> {..}

// Convenience
fn optional(self) -> impl Parser<Option<A>> {..}
fn many(self) -> impl Parser<Vec<A>> {..}
fn some(self, error: &'static str) -> impl Parser<Vec<A>> {..}
fn collect<C: FromIterator<A>>(self) -> impl Parser<C> {..}
fn parse(self, impl Fn(A) -> Result<B, E>) -> impl Parser<B> {..}

// Required to run
fn run(self) -> A {..}
}
```

<!-- end_slide -->

# dynamic completionm

- Easy to implement

# Conclusions

- High level overview of your app/API
- Composing with CT reduces number of combinations from N^2 to N
- Users don't have to know about it :)

# See also

- https://rustmagazine.org/issue-2/applicative-parsing/
- https://en.wikipedia.org/wiki/Category_theory
- https://crates.io/crates/bpaf
- https://github.com/pacak
- @manpacket@functional.cafe
7 changes: 7 additions & 0 deletions content/2024-09-17/http-for-lunch/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions content/2024-09-17/http-for-lunch/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "http-for-lunch"
version = "0.1.0"
edition = "2021"

[dependencies]
48 changes: 48 additions & 0 deletions content/2024-09-17/http-for-lunch/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# HTTP for Lunch

This is an HTTP server which was built during the [September Rust for Lunch meetup](https://lunch.rs/meetups/2024-09-17/).

The code has been left exactly as it was at the end of the meet-up, `dbg!` statements and all!

## Building & running

Normal `cargo` behaviour:

```sh
cargo run
```

## Testing

We ran the following requests against the server.

Happy path:

```sh
$ curl -D - http://127.0.0.1:8080/hello
HTTP/1.1 200 Rust for Lunch
Content-Length: 13

Hello, World!
```

Method not allowed:

```sh
$ curl -X POST -D - http://127.0.0.1:8080/hello
HTTP/1.1 405 Method Not Allowed
```

HTTP version not supported:

```sh
$ curl --http1.0 -D - http://127.0.0.1:8080/hello
HTTP/1.1 505 HTTP Version not supported
```

Not found:

```sh
$ curl -D - http://127.0.0.1:8080/bye
HTTP/1.1 404 Not Found
```
Loading

0 comments on commit b7e8e06

Please sign in to comment.