]> git.taranathan.com Git - blog.git/commitdiff
gleam blog, plus syntax highlighting stuff!
authormoo <moogoesmeow123@gmail.com>
Tue, 16 Jun 2026 01:06:19 +0000 (18:06 -0700)
committermoo <moogoesmeow123@gmail.com>
Tue, 16 Jun 2026 01:06:19 +0000 (18:06 -0700)
content/blog/gleam.md [new file with mode: 0644]
justfile
public/blog/gleam/index.html [new file with mode: 0644]
public/blog/index.html
public/sitemap.xml
public/styles.css
sass/styles.scss

diff --git a/content/blog/gleam.md b/content/blog/gleam.md
new file mode 100644 (file)
index 0000000..a349d0b
--- /dev/null
@@ -0,0 +1,119 @@
++++
+title = "gleam"
+date = 2026-06-15
++++
+
+Last wednesday, when I was boarding my flight to Junior Men's National Team field hockey tryouts(wish me luck!), I started to try out the Gleam programming language. It is really fascinating. It looks like rust(without mutability or imperativeness), but compiles down to erlang and javascript. It also has a bunch of quality of life features for functional programming that I would love to have in rust, like the pipe operator or the ease of its partial function application. For example, in gleam, you can just do this to partially apply a function:
+```gleam
+fn add(x: Int, y: Int) -> Int {
+  x + y
+}
+
+fn main() {
+  let add_one = add(_, 1)
+  assert add_one(3) == 4
+}
+```
+It's so simple! Also, the pipe operator works just like you'd expect from any other functional language:
+```gleam
+fn main() {
+  assert 5 == { 1 |> add_one |> add_three }
+}
+```
+I feel like they also made a couple design decisions in a pythonic manner. For example, in the code above, you can see that to specify and clarify order of operations, instead of using parentheses it uses curly braces. This is because like rust, gleam is an expression based language, and therefore, code blocks return values without having to manually insert a return statement. It may seem like this is just trying to be different just to be different, but I really like this, and is just an interesting consequence of being expression based, and it removes unnecessary options to do functionally the same thing. In fact, you can do this in rust(another expression based language) as well!
+
+```rust
+fn main() {
+  let x = 3 * { 2 + 3 };
+  assert_eq!(x, 15);
+}
+```
+Now, back to the story. So I was having a bunch of stupid problems with the airport wifi, and my phone's hotspot was bugging out as well, so I was completely offline for like 5 hrs, even before I got on the flight. (this was also kinda because my flight got delayed by 4 hrs, because they switched the plane we were going to be taking to a different one.) So during that time, I started to try to see what I could get done with the language. Gleam's package manager/build tool/whatever cli is really nice, and it caches all the data of dependencies added to the project previously. So, basically what I did was before I left home, I added a bunch of packages that I saw were pretty commonly used in the community, and a web framework. So what I did while off the internet was just tinker around with the language and the libraries, trying to figure out what to do based on LSP Hover tips and examples. I wanted to figure out how to do argument parsing and a simple web application. For the argument parsing thing, I kinda just messed around with the argv library, and never actually tried out glint, the more full featured argument parser. The entire argv library is basically just a single function, `argv.load()`, that returns a list of strings, which are the arguments to the program. But this really beautifully showcases some of the power of gleam's pattern matching:
+```gleam
+pub fn main() {
+  let args: List(String) = argv.load().arguments
+  case args {
+    ["greet", name] -> io.println("hello " <> name <> "!")
+    ["do-thing", file_path, "-o", ..output_files] -> do_thing(file_path, output_files)
+    _ -> io.println("bad args")
+  }
+}
+```
+I think that this is probably my favorite way to do simple command line argument parsing, even if it isn't the most effective or feature complete. I guess if you want to do that, there is the glint library that I mentioned previously, but I haven't really got around to exploring that yet.
+
+The second part of my little adventure was trying to make a little web app in the programming language. I ended up learning about an actual frontend framework that uses the fact that the language can compile to Javascript (lustre), but I already had hacked together a simple html/css/js frontend by that point. The finished project is at [files.taranathan.com](https://files.taranathan.com), so you guys can check out what it ended up like there. (Side note: During the process, I ended up moving a lot of the things I was hosting on my web server from apache2 to Caddy, and it was really interesting! I think caddy is really cool, and it is sooooo much simpler to configure for the really simple things I am doing. I basically just host a wordpress site, run a gitweb site(cgi), run my site(just static hosting), and a couple of reverse proxies to expose my web-related projects. All I had to do was recompile caddy from source with a module for cgi.) So, if you went to the link, you would see that it is just a really simple file hosting/sharing site. The whole page is basically just a form asking for a file, a file name, and a password(I might give it to you if you ask really nicely \\(°^°)/). So this is basically the entry point of the web server. The argument `file_path` is the path to where to store and serve the files from, and `passwords` is just a list of the valid passwords(i know, not very secure). 
+```gleam
+fn web(file_path: String, passwords: List(String)) -> Nil {
+  wisp.configure_logger()
+
+  let secret_key_base =
+    result.lazy_unwrap(envoy.get("SECRET_KEY_BASE"), fn() {
+      wisp.random_string(64)
+    })
+
+  let assert Ok(_) =
+    router.handle_request(_, file_path, passwords)
+    |> wisp_mist.handler(secret_key_base)
+    |> mist.new
+    |> mist.port(
+      envoy.get("PORT") |> result.try(int.parse) |> result.unwrap(8000),
+    )
+    |> mist.start
+
+  process.sleep_forever()
+}
+```
+The router module is mine, and I'll talk about it later. This might look pretty wierd to you, if you come from an imperative or OOP background, so I'll go through it line by line. First we configure the logger, and then generate a `secret_key_base` either from an env variable or a random string. Then, we get to the `let assert Ok(_) = ...`. This basically means that the result of the expression in front of it is of the `result` type/enum, and it must be of the Ok variant, and we do not care about what is inside of the `Ok`. Another thing that might be confusing is the `router.handle_request(_, file_path, passwords)`. As I wrote about above, this is an example of partial function application. The actual function signature of router.handle_request is:
+```gleam
+pub fn handle_request(
+  req: wisp.Request,
+  file_path: String,
+  passwords: List(String),
+) -> wisp.Response {
+```
+So what we are doing here is we are basically filling in `file_path` and `passwords` and changing the function signature into `fn(wisp.Request) -> wisp.Response`. And then after that, we just pipe it into a couple of builder functions and then start it. After starting it, we tell the main process to sleep forever, because `mist.start` starts the process on another thread. 
+
+Now lets take a look at the main route handler function:
+```gleam
+pub fn handle_request(
+  req: wisp.Request,
+  file_path: String,
+  passwords: List(String),
+) -> wisp.Response {
+  use req <- middleware(req)
+
+  case req.path {
+    "/index.html" -> main_page(req)
+    "/" -> main_page(req)
+    "/favicon.ico" -> favicon(req)
+    "/styles.css" -> {
+      use <- wisp.serve_static(req, "/styles.css", "./static/styles.css")
+      wisp.ok()
+    }
+    "/new/" <> _path -> upload(req, passwords)
+    "/list" -> list(req, file_path)
+    "/files/" <> path -> files(req, "/" <> path, file_path)
+    _ -> wisp.not_found()
+  }
+}
+```
+So, the first line basically just sets up some basic middleware stuff. The use statement in gleam is pretty wierd and hard to wrap your head around, but it really is just some syntax sugar. For example, if we just removed the use statement from our function, it would look like this:
+```gleam
+pub fn handle_request(
+  req: wisp.Request,
+  file_path: String,
+  passwords: List(String),
+) -> wisp.Response {
+  middleware(req, fn(req) {
+
+    case req.path {
+      //truncated
+    }
+  })
+}
+```
+So basically, what it does is turn the rest of the function/block you are working in into a lambda function to be passed into the other function in the use statement. This is really useful in the context of middleware, because it perfectly matches the stack-based way that middleware works, where callbacks get passed up and down the stack. The middleware in this case is just doing some logging and crash rescuing and some other stuff, nothing super interesting.
+
+I guess that's kinda all the interesting stuff that I have for you today, the rest of the code is not very noteworthy, but if you want to look at it you can go to the repo at [git.taranathan.com](https://git.taranathan.com), under the name filestuffs. I really thought it was really fun and intuitive while working on this little toy project, and I find it pretty cool and interesting that I was able to complete this thing without really having to look at docs at all really, just relying on LSP autocomplete and hover, and I could get something that worked pretty well, and looked pretty similar to the examples!
+
+Anyways, have a nice rest of your day, random internet stranger!
index 00be3401f2e11568adb35a4a3e5ad8a5343fe35b..609c77279f6b3f5f78004af1ffd329383927a7af 100644 (file)
--- a/justfile
+++ b/justfile
@@ -20,7 +20,7 @@ deploy:
     git add -A
     git commit -a
     git push
-    ssh root@taranathan.com 'cd /root/blog/ && git fetch && git reset --hard origin/main'
+    ssh root@taranathan.com 'cd /root/blog/ && git fetch --all && git reset --hard origin/main'
 
 deploy-raw:
-    ssh root@taranathan.com 'cd /root/blog/ && git fetch && git reset --hard origin/main'
+    ssh root@taranathan.com 'cd /root/blog/ && git fetch --all && git reset --hard origin/main'
diff --git a/public/blog/gleam/index.html b/public/blog/gleam/index.html
new file mode 100644 (file)
index 0000000..d034d6e
--- /dev/null
@@ -0,0 +1,161 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+  <meta charset="utf-8">
+  <title>gleam - MyBlog</title>
+  <link rel="icon" type="image/x-icon" href="https://taranathan.com/favicon.ico">
+  <link rel="stylesheet" href="https://taranathan.com/styles.css">
+</head>
+
+<body>
+  <div class="container">
+    <div class="kaomoji-sidebar kaomoji-left">
+      <div class="scroll-text" id="kaomoji-left"></div>
+    </div>
+
+    <main class="content">
+      <header class="header">
+        <h1 href="/">My Blog</h1>
+        <nav class="nav">
+          <a href="/">Home</a>
+          <a href="/blog">Blog</a>
+          <a href="/miniblog">Miniblog</a>
+          <a href="/boo" target="_blank">Fun</a>
+        </nav>
+      </header>
+
+      <section class="content-body">
+        
+  <article class="post">
+    <h1 class="title">gleam</h1>
+    <p class="subtitle"><strong>2026-06-15</strong></p>
+    <div class="post-content"><p>Last wednesday, when I was boarding my flight to Junior Men’s National Team field hockey tryouts(wish me luck!), I started to try out the Gleam programming language. It is really fascinating. It looks like rust(without mutability or imperativeness), but compiles down to erlang and javascript. It also has a bunch of quality of life features for functional programming that I would love to have in rust, like the pipe operator or the ease of its partial function application. For example, in gleam, you can just do this to partially apply a function:</p>
+<pre class="giallo" style="color: #CDD6F4; background-color: #1E1E2E;"><code data-lang="gleam"><span class="giallo-l"><span style="color: #CBA6F7;">fn</span><span style="color: #89B4FA;font-style: italic;"> add</span><span>(</span><span style="color: #EBA0AC;font-style: italic;">x: </span><span style="color: #F9E2AF;font-style: italic;">Int</span><span>, </span><span style="color: #EBA0AC;font-style: italic;">y: </span><span style="color: #F9E2AF;font-style: italic;">Int</span><span>)</span><span style="color: #94E2D5;"> -&gt;</span><span style="color: #F9E2AF;font-style: italic;"> Int</span><span> {</span></span>
+<span class="giallo-l"><span>  x </span><span style="color: #94E2D5;">+</span><span> y</span></span>
+<span class="giallo-l"><span>}</span></span>
+<span class="giallo-l"></span>
+<span class="giallo-l"><span style="color: #CBA6F7;">fn</span><span style="color: #89B4FA;font-style: italic;"> main</span><span>() {</span></span>
+<span class="giallo-l"><span style="color: #CBA6F7;">  let</span><span> add_one </span><span style="color: #94E2D5;">=</span><span style="color: #89B4FA;font-style: italic;"> add</span><span>(</span><span style="color: #9399B2;font-style: italic;">_</span><span>, </span><span style="color: #FAB387;">1</span><span>)</span></span>
+<span class="giallo-l"><span style="color: #CBA6F7;">  assert</span><span style="color: #89B4FA;font-style: italic;"> add_one</span><span>(</span><span style="color: #FAB387;">3</span><span>)</span><span style="color: #94E2D5;"> ==</span><span style="color: #FAB387;"> 4</span></span>
+<span class="giallo-l"><span>}</span></span></code></pre>
+<p>It’s so simple! Also, the pipe operator works just like you’d expect from any other functional language:</p>
+<pre class="giallo" style="color: #CDD6F4; background-color: #1E1E2E;"><code data-lang="gleam"><span class="giallo-l"><span style="color: #CBA6F7;">fn</span><span style="color: #89B4FA;font-style: italic;"> main</span><span>() {</span></span>
+<span class="giallo-l"><span style="color: #CBA6F7;">  assert</span><span style="color: #FAB387;"> 5</span><span style="color: #94E2D5;"> ==</span><span> { </span><span style="color: #FAB387;">1</span><span style="color: #94E2D5;"> |&gt;</span><span> add_one </span><span style="color: #94E2D5;">|&gt;</span><span> add_three }</span></span>
+<span class="giallo-l"><span>}</span></span></code></pre>
+<p>I feel like they also made a couple design decisions in a pythonic manner. For example, in the code above, you can see that to specify and clarify order of operations, instead of using parentheses it uses curly braces. This is because like rust, gleam is an expression based language, and therefore, code blocks return values without having to manually insert a return statement. It may seem like this is just trying to be different just to be different, but I really like this, and is just an interesting consequence of being expression based, and it removes unnecessary options to do functionally the same thing. In fact, you can do this in rust(another expression based language) as well!</p>
+<pre class="giallo" style="color: #CDD6F4; background-color: #1E1E2E;"><code data-lang="rust"><span class="giallo-l"><span style="color: #CBA6F7;">fn</span><span style="color: #89B4FA;font-style: italic;"> main</span><span style="color: #9399B2;">() {</span></span>
+<span class="giallo-l"><span style="color: #CBA6F7;">  let</span><span> x</span><span style="color: #94E2D5;"> =</span><span style="color: #FAB387;"> 3</span><span style="color: #94E2D5;"> *</span><span style="color: #9399B2;"> {</span><span style="color: #FAB387;"> 2</span><span style="color: #94E2D5;"> +</span><span style="color: #FAB387;"> 3</span><span style="color: #9399B2;"> };</span></span>
+<span class="giallo-l"><span style="color: #89B4FA;font-style: italic;">  assert_eq!</span><span style="color: #9399B2;">(</span><span>x</span><span style="color: #9399B2;">,</span><span style="color: #FAB387;"> 15</span><span style="color: #9399B2;">);</span></span>
+<span class="giallo-l"><span style="color: #9399B2;">}</span></span></code></pre>
+<p>Now, back to the story. So I was having a bunch of stupid problems with the airport wifi, and my phone’s hotspot was bugging out as well, so I was completely offline for like 5 hrs, even before I got on the flight. (this was also kinda because my flight got delayed by 4 hrs, because they switched the plane we were going to be taking to a different one.) So during that time, I started to try to see what I could get done with the language. Gleam’s package manager/build tool/whatever cli is really nice, and it caches all the data of dependencies added to the project previously. So, basically what I did was before I left home, I added a bunch of packages that I saw were pretty commonly used in the community, and a web framework. So what I did while off the internet was just tinker around with the language and the libraries, trying to figure out what to do based on LSP Hover tips and examples. I wanted to figure out how to do argument parsing and a simple web application. For the argument parsing thing, I kinda just messed around with the argv library, and never actually tried out glint, the more full featured argument parser. The entire argv library is basically just a single function, <code>argv.load()</code>, that returns a list of strings, which are the arguments to the program. But this really beautifully showcases some of the power of gleam’s pattern matching:</p>
+<pre class="giallo" style="color: #CDD6F4; background-color: #1E1E2E;"><code data-lang="gleam"><span class="giallo-l"><span style="color: #CBA6F7;">pub fn</span><span style="color: #89B4FA;font-style: italic;"> main</span><span>() {</span></span>
+<span class="giallo-l"><span style="color: #CBA6F7;">  let</span><span style="color: #EBA0AC;font-style: italic;"> args: </span><span style="color: #F9E2AF;font-style: italic;">List</span><span>(</span><span style="color: #F9E2AF;font-style: italic;">String</span><span>) </span><span style="color: #94E2D5;">=</span><span> argv.</span><span style="color: #89B4FA;font-style: italic;">load</span><span>().arguments</span></span>
+<span class="giallo-l"><span style="color: #CBA6F7;">  case</span><span> args {</span></span>
+<span class="giallo-l"><span>    [</span><span style="color: #A6E3A1;">&quot;greet&quot;</span><span>, name] </span><span style="color: #94E2D5;">-&gt;</span><span> io.</span><span style="color: #89B4FA;font-style: italic;">println</span><span>(</span><span style="color: #A6E3A1;">&quot;hello &quot;</span><span style="color: #94E2D5;"> &lt;&gt;</span><span> name </span><span style="color: #94E2D5;">&lt;&gt;</span><span style="color: #A6E3A1;"> &quot;!&quot;</span><span>)</span></span>
+<span class="giallo-l"><span>    [</span><span style="color: #A6E3A1;">&quot;do-thing&quot;</span><span>, file_path, </span><span style="color: #A6E3A1;">&quot;-o&quot;</span><span>, </span><span style="color: #94E2D5;">..</span><span>output_files] </span><span style="color: #94E2D5;">-&gt;</span><span style="color: #89B4FA;font-style: italic;"> do_thing</span><span>(file_path, output_files)</span></span>
+<span class="giallo-l"><span style="color: #9399B2;font-style: italic;">    _</span><span style="color: #94E2D5;"> -&gt;</span><span> io.</span><span style="color: #89B4FA;font-style: italic;">println</span><span>(</span><span style="color: #A6E3A1;">&quot;bad args&quot;</span><span>)</span></span>
+<span class="giallo-l"><span>  }</span></span>
+<span class="giallo-l"><span>}</span></span></code></pre>
+<p>I think that this is probably my favorite way to do simple command line argument parsing, even if it isn’t the most effective or feature complete. I guess if you want to do that, there is the glint library that I mentioned previously, but I haven’t really got around to exploring that yet.</p>
+<p>The second part of my little adventure was trying to make a little web app in the programming language. I ended up learning about an actual frontend framework that uses the fact that the language can compile to Javascript (lustre), but I already had hacked together a simple html/css/js frontend by that point. The finished project is at <a rel="external" href="https://files.taranathan.com">files.taranathan.com</a>, so you guys can check out what it ended up like there. (Side note: During the process, I ended up moving a lot of the things I was hosting on my web server from apache2 to Caddy, and it was really interesting! I think caddy is really cool, and it is sooooo much simpler to configure for the really simple things I am doing. I basically just host a wordpress site, run a gitweb site(cgi), run my site(just static hosting), and a couple of reverse proxies to expose my web-related projects. All I had to do was recompile caddy from source with a module for cgi.) So, if you went to the link, you would see that it is just a really simple file hosting/sharing site. The whole page is basically just a form asking for a file, a file name, and a password(I might give it to you if you ask really nicely \(°^°)/). So this is basically the entry point of the web server. The argument <code>file_path</code> is the path to where to store and serve the files from, and <code>passwords</code> is just a list of the valid passwords(i know, not very secure).</p>
+<pre class="giallo" style="color: #CDD6F4; background-color: #1E1E2E;"><code data-lang="gleam"><span class="giallo-l"><span style="color: #CBA6F7;">fn</span><span style="color: #89B4FA;font-style: italic;"> web</span><span>(</span><span style="color: #EBA0AC;font-style: italic;">file_path: </span><span style="color: #F9E2AF;font-style: italic;">String</span><span>, </span><span style="color: #EBA0AC;font-style: italic;">passwords: </span><span style="color: #F9E2AF;font-style: italic;">List</span><span>(</span><span style="color: #F9E2AF;font-style: italic;">String</span><span>)) </span><span style="color: #94E2D5;">-&gt;</span><span style="color: #F9E2AF;font-style: italic;"> Nil</span><span> {</span></span>
+<span class="giallo-l"><span>  wisp.</span><span style="color: #89B4FA;font-style: italic;">configure_logger</span><span>()</span></span>
+<span class="giallo-l"></span>
+<span class="giallo-l"><span style="color: #CBA6F7;">  let</span><span> secret_key_base </span><span style="color: #94E2D5;">=</span></span>
+<span class="giallo-l"><span>    result.</span><span style="color: #89B4FA;font-style: italic;">lazy_unwrap</span><span>(envoy.</span><span style="color: #89B4FA;font-style: italic;">get</span><span>(</span><span style="color: #A6E3A1;">&quot;SECRET_KEY_BASE&quot;</span><span>), </span><span style="color: #CBA6F7;">fn</span><span>() {</span></span>
+<span class="giallo-l"><span>      wisp.</span><span style="color: #89B4FA;font-style: italic;">random_string</span><span>(</span><span style="color: #FAB387;">64</span><span>)</span></span>
+<span class="giallo-l"><span>    })</span></span>
+<span class="giallo-l"></span>
+<span class="giallo-l"><span style="color: #CBA6F7;">  let assert</span><span style="color: #F9E2AF;font-style: italic;"> Ok</span><span>(</span><span style="color: #9399B2;font-style: italic;">_</span><span>) </span><span style="color: #94E2D5;">=</span></span>
+<span class="giallo-l"><span>    router.</span><span style="color: #89B4FA;font-style: italic;">handle_request</span><span>(</span><span style="color: #9399B2;font-style: italic;">_</span><span>, file_path, passwords)</span></span>
+<span class="giallo-l"><span style="color: #94E2D5;">    |&gt;</span><span> wisp_mist.</span><span style="color: #89B4FA;font-style: italic;">handler</span><span>(secret_key_base)</span></span>
+<span class="giallo-l"><span style="color: #94E2D5;">    |&gt;</span><span> mist.new</span></span>
+<span class="giallo-l"><span style="color: #94E2D5;">    |&gt;</span><span> mist.</span><span style="color: #89B4FA;font-style: italic;">port</span><span>(</span></span>
+<span class="giallo-l"><span>      envoy.</span><span style="color: #89B4FA;font-style: italic;">get</span><span>(</span><span style="color: #A6E3A1;">&quot;PORT&quot;</span><span>)</span><span style="color: #94E2D5;"> |&gt;</span><span> result.</span><span style="color: #89B4FA;font-style: italic;">try</span><span>(int.parse)</span><span style="color: #94E2D5;"> |&gt;</span><span> result.</span><span style="color: #89B4FA;font-style: italic;">unwrap</span><span>(</span><span style="color: #FAB387;">8000</span><span>),</span></span>
+<span class="giallo-l"><span>    )</span></span>
+<span class="giallo-l"><span style="color: #94E2D5;">    |&gt;</span><span> mist.start</span></span>
+<span class="giallo-l"></span>
+<span class="giallo-l"><span>  process.</span><span style="color: #89B4FA;font-style: italic;">sleep_forever</span><span>()</span></span>
+<span class="giallo-l"><span>}</span></span></code></pre>
+<p>The router module is mine, and I’ll talk about it later. This might look pretty wierd to you, if you come from an imperative or OOP background, so I’ll go through it line by line. First we configure the logger, and then generate a <code>secret_key_base</code> either from an env variable or a random string. Then, we get to the <code>let assert Ok(_) = ...</code>. This basically means that the result of the expression in front of it is of the <code>result</code> type/enum, and it must be of the Ok variant, and we do not care about what is inside of the <code>Ok</code>. Another thing that might be confusing is the <code>router.handle_request(_, file_path, passwords)</code>. As I wrote about above, this is an example of partial function application. The actual function signature of router.handle_request is:</p>
+<pre class="giallo" style="color: #CDD6F4; background-color: #1E1E2E;"><code data-lang="gleam"><span class="giallo-l"><span style="color: #CBA6F7;">pub fn</span><span style="color: #89B4FA;font-style: italic;"> handle_request</span><span>(</span></span>
+<span class="giallo-l"><span style="color: #EBA0AC;font-style: italic;">  req: </span><span>wisp.</span><span style="color: #F9E2AF;font-style: italic;">Request</span><span>,</span></span>
+<span class="giallo-l"><span style="color: #EBA0AC;font-style: italic;">  file_path: </span><span style="color: #F9E2AF;font-style: italic;">String</span><span>,</span></span>
+<span class="giallo-l"><span style="color: #EBA0AC;font-style: italic;">  passwords: </span><span style="color: #F9E2AF;font-style: italic;">List</span><span>(</span><span style="color: #F9E2AF;font-style: italic;">String</span><span>),</span></span>
+<span class="giallo-l"><span>) </span><span style="color: #94E2D5;">-&gt;</span><span> wisp.</span><span style="color: #F9E2AF;font-style: italic;">Response</span><span> {</span></span></code></pre>
+<p>So what we are doing here is we are basically filling in <code>file_path</code> and <code>passwords</code> and changing the function signature into <code>fn(wisp.Request) -&gt; wisp.Response</code>. And then after that, we just pipe it into a couple of builder functions and then start it. After starting it, we tell the main process to sleep forever, because <code>mist.start</code> starts the process on another thread.</p>
+<p>Now lets take a look at the main route handler function:</p>
+<pre class="giallo" style="color: #CDD6F4; background-color: #1E1E2E;"><code data-lang="gleam"><span class="giallo-l"><span style="color: #CBA6F7;">pub fn</span><span style="color: #89B4FA;font-style: italic;"> handle_request</span><span>(</span></span>
+<span class="giallo-l"><span style="color: #EBA0AC;font-style: italic;">  req: </span><span>wisp.</span><span style="color: #F9E2AF;font-style: italic;">Request</span><span>,</span></span>
+<span class="giallo-l"><span style="color: #EBA0AC;font-style: italic;">  file_path: </span><span style="color: #F9E2AF;font-style: italic;">String</span><span>,</span></span>
+<span class="giallo-l"><span style="color: #EBA0AC;font-style: italic;">  passwords: </span><span style="color: #F9E2AF;font-style: italic;">List</span><span>(</span><span style="color: #F9E2AF;font-style: italic;">String</span><span>),</span></span>
+<span class="giallo-l"><span>) </span><span style="color: #94E2D5;">-&gt;</span><span> wisp.</span><span style="color: #F9E2AF;font-style: italic;">Response</span><span> {</span></span>
+<span class="giallo-l"><span style="color: #CBA6F7;">  use</span><span> req </span><span style="color: #94E2D5;">&lt;-</span><span style="color: #89B4FA;font-style: italic;"> middleware</span><span>(req)</span></span>
+<span class="giallo-l"></span>
+<span class="giallo-l"><span style="color: #CBA6F7;">  case</span><span> req.path {</span></span>
+<span class="giallo-l"><span style="color: #A6E3A1;">    &quot;/index.html&quot;</span><span style="color: #94E2D5;"> -&gt;</span><span style="color: #89B4FA;font-style: italic;"> main_page</span><span>(req)</span></span>
+<span class="giallo-l"><span style="color: #A6E3A1;">    &quot;/&quot;</span><span style="color: #94E2D5;"> -&gt;</span><span style="color: #89B4FA;font-style: italic;"> main_page</span><span>(req)</span></span>
+<span class="giallo-l"><span style="color: #A6E3A1;">    &quot;/favicon.ico&quot;</span><span style="color: #94E2D5;"> -&gt;</span><span style="color: #89B4FA;font-style: italic;"> favicon</span><span>(req)</span></span>
+<span class="giallo-l"><span style="color: #A6E3A1;">    &quot;/styles.css&quot;</span><span style="color: #94E2D5;"> -&gt;</span><span> {</span></span>
+<span class="giallo-l"><span style="color: #CBA6F7;">      use</span><span style="color: #94E2D5;"> &lt;-</span><span> wisp.</span><span style="color: #89B4FA;font-style: italic;">serve_static</span><span>(req, </span><span style="color: #A6E3A1;">&quot;/styles.css&quot;</span><span>, </span><span style="color: #A6E3A1;">&quot;./static/styles.css&quot;</span><span>)</span></span>
+<span class="giallo-l"><span>      wisp.</span><span style="color: #89B4FA;font-style: italic;">ok</span><span>()</span></span>
+<span class="giallo-l"><span>    }</span></span>
+<span class="giallo-l"><span style="color: #A6E3A1;">    &quot;/new/&quot;</span><span style="color: #94E2D5;"> &lt;&gt;</span><span style="color: #9399B2;font-style: italic;"> _path</span><span style="color: #94E2D5;"> -&gt;</span><span style="color: #89B4FA;font-style: italic;"> upload</span><span>(req, passwords)</span></span>
+<span class="giallo-l"><span style="color: #A6E3A1;">    &quot;/list&quot;</span><span style="color: #94E2D5;"> -&gt;</span><span style="color: #89B4FA;font-style: italic;"> list</span><span>(req, file_path)</span></span>
+<span class="giallo-l"><span style="color: #A6E3A1;">    &quot;/files/&quot;</span><span style="color: #94E2D5;"> &lt;&gt;</span><span> path </span><span style="color: #94E2D5;">-&gt;</span><span style="color: #89B4FA;font-style: italic;"> files</span><span>(req, </span><span style="color: #A6E3A1;">&quot;/&quot;</span><span style="color: #94E2D5;"> &lt;&gt;</span><span> path, file_path)</span></span>
+<span class="giallo-l"><span style="color: #9399B2;font-style: italic;">    _</span><span style="color: #94E2D5;"> -&gt;</span><span> wisp.</span><span style="color: #89B4FA;font-style: italic;">not_found</span><span>()</span></span>
+<span class="giallo-l"><span>  }</span></span>
+<span class="giallo-l"><span>}</span></span></code></pre>
+<p>So, the first line basically just sets up some basic middleware stuff. The use statement in gleam is pretty wierd and hard to wrap your head around, but it really is just some syntax sugar. For example, if we just removed the use statement from our function, it would look like this:</p>
+<pre class="giallo" style="color: #CDD6F4; background-color: #1E1E2E;"><code data-lang="gleam"><span class="giallo-l"><span style="color: #CBA6F7;">pub fn</span><span style="color: #89B4FA;font-style: italic;"> handle_request</span><span>(</span></span>
+<span class="giallo-l"><span style="color: #EBA0AC;font-style: italic;">  req: </span><span>wisp.</span><span style="color: #F9E2AF;font-style: italic;">Request</span><span>,</span></span>
+<span class="giallo-l"><span style="color: #EBA0AC;font-style: italic;">  file_path: </span><span style="color: #F9E2AF;font-style: italic;">String</span><span>,</span></span>
+<span class="giallo-l"><span style="color: #EBA0AC;font-style: italic;">  passwords: </span><span style="color: #F9E2AF;font-style: italic;">List</span><span>(</span><span style="color: #F9E2AF;font-style: italic;">String</span><span>),</span></span>
+<span class="giallo-l"><span>) </span><span style="color: #94E2D5;">-&gt;</span><span> wisp.</span><span style="color: #F9E2AF;font-style: italic;">Response</span><span> {</span></span>
+<span class="giallo-l"><span style="color: #89B4FA;font-style: italic;">  middleware</span><span>(req, </span><span style="color: #CBA6F7;">fn</span><span>(req) {</span></span>
+<span class="giallo-l"></span>
+<span class="giallo-l"><span style="color: #CBA6F7;">    case</span><span> req.path {</span></span>
+<span class="giallo-l"><span style="color: #9399B2;font-style: italic;">      //truncated</span></span>
+<span class="giallo-l"><span>    }</span></span>
+<span class="giallo-l"><span>  })</span></span>
+<span class="giallo-l"><span>}</span></span></code></pre>
+<p>So basically, what it does is turn the rest of the function/block you are working in into a lambda function to be passed into the other function in the use statement. This is really useful in the context of middleware, because it perfectly matches the stack-based way that middleware works, where callbacks get passed up and down the stack. The middleware in this case is just doing some logging and crash rescuing and some other stuff, nothing super interesting.</p>
+<p>I guess that’s kinda all the interesting stuff that I have for you today, the rest of the code is not very noteworthy, but if you want to look at it you can go to the repo at <a rel="external" href="https://git.taranathan.com">git.taranathan.com</a>, under the name filestuffs. I really thought it was really fun and intuitive while working on this little toy project, and I find it pretty cool and interesting that I was able to complete this thing without really having to look at docs at all really, just relying on LSP autocomplete and hover, and I could get something that worked pretty well, and looked pretty similar to the examples!</p>
+<p>Anyways, have a nice rest of your day, random internet stranger!</p>
+</div>
+  </article>
+
+      </section>
+    </main>
+
+    <div class="kaomoji-sidebar kaomoji-right">
+      <div class="scroll-text" id="kaomoji-right"></div>
+    </div>
+  </div>
+
+  <script>
+    // Smooth scrolling for internal links
+    document.querySelectorAll('nav a').forEach(anchor => {
+      anchor.addEventListener('click', function (e) {
+        const href = this.getAttribute('href');
+        if (href && href.startsWith('#')) {
+          e.preventDefault();
+          document.querySelector(href).scrollIntoView({ behavior: 'smooth' });
+        }
+      });
+    });
+
+    // Fetch kaomoji text if available
+    fetch('https://taranathan.com/kaomoji.txt').then(r => { if (r.ok) return r.text(); }).then(data => {
+      if (data) {
+        const l = document.getElementById('kaomoji-left');
+        const rgt = document.getElementById('kaomoji-right');
+        if (l) l.textContent = data;
+        if (rgt) rgt.textContent = data;
+      }
+    }).catch(()=>{});
+  </script>
+</body>
+
+</html>
index 6326784eb0c1d61ee3d9be5e67ebe73669968d8c..f71db53b7b24e078e2fe74fa483cf704940e788f 100644 (file)
   <h2 class="section-title">Blog posts</h2>
   <ul class="post-list">
     
+      <li class="post-list-item">
+        <a href="https://taranathan.com/blog/gleam/">gleam</a>
+      <p class="subtitle""> — 2026-06-15</p>
+      
+    </li>
+  
       <li class="post-list-item">
         <a href="https://taranathan.com/blog/second/">second post</a>
       <p class="subtitle""> — 2026-06-01</p>
index db11a29beeba604f440df4ee49678f819b464fa1..43c19d4152e46d9e9c44738439200c1ff95fd83d 100644 (file)
         <loc>https://taranathan.com/blog/first/</loc>
         <lastmod>2026-04-16</lastmod>
     </url>
+    <url>
+        <loc>https://taranathan.com/blog/gleam/</loc>
+        <lastmod>2026-06-15</lastmod>
+    </url>
     <url>
         <loc>https://taranathan.com/blog/second/</loc>
         <lastmod>2026-06-01</lastmod>
index 519276d97f25d9603b400e82e199248b964390bb..91b035319b744d106b0d74369b68f0ee1eae9c62 100644 (file)
@@ -1 +1 @@
-html{background-color:linen}*{margin:0;padding:0;box-sizing:border-box;border-radius:5%;background-color:linen}@font-face{font-family:"Funnel";src:url("fonts/Funnel_Display/static/FunnelDisplay-SemiBold.ttf") format("truetype")}@font-face{font-family:"BoldFunnel";src:url("fonts/Funnel_Display/static/FunnelDisplay-Bold.ttf") format("truetype")}@font-face{font-family:"Dosis";src:url("fonts/Dosis/static/Dosis-Medium.ttf") format("truetype")}h1,h2,h3,h4,h5,h6{font-family:Funnel,Arial}p,a{font-family:Dosis,Arial}body{min-height:100vh;overflow-x:hidden;background:linen;display:flex}.container{display:flex;width:100%;min-height:100vh;position:relative}.kaomoji-sidebar{position:fixed;top:0;height:100vh;width:60px;font-size:24px;display:flex;align-items:center;justify-content:center;background:linen;overflow:hidden;z-index:100}.kaomoji-sidebar .scroll-text{transform:rotate(.25turn);white-space:nowrap;animation:scroll 90s linear infinite}.kaomoji-left{left:0;border-right:1px solid #000}.kaomoji-right{right:0;border-left:1px solid #000}.kaomoji-right .scroll-text{transform:rotate(.5turn)}@keyframes scroll{0%{transform:rotate(90deg) translateX(-64%)}100%{transform:rotate(90deg) translateX(80%)}}.content{flex:1;margin:0 60px;padding:20px;width:calc(100% - 120px);max-width:1400px;margin-left:auto;margin-right:auto;position:relative}.section-container{position:relative;margin-bottom:40px}.header{position:relative;padding:40px;margin-left:40px;background:rgba(0,0,0,0);margin-bottom:40px}.header h1{font-family:BoldFunnel}.header .nav .prooject{text-size-adjust:.5}.header::after{content:"";position:absolute;bottom:-20px;left:-60px;width:calc(100% + 120px);height:2px;background:#000;transform:rotate(-3deg);transform-origin:center}.about-section{position:relative;padding:40px;background:rgba(0,0,0,0);margin-bottom:40px;margin-left:20px}.project-section{position:relative;padding:40px;background:linen;margin-bottom:40px;margin-left:20px;display:grid;grid-template-columns:1fr 1fr;gap:40px;align-items:center}.project-section::before{content:"";position:absolute;top:-20px;left:-60px;width:calc(100% + 120px);height:2px;background:#000;transform:rotate(3deg);transform-origin:center}.more-section{position:relative;padding:40px;margin-left:20px;background:linen}.more-section::before{content:"";position:absolute;top:-20px;left:-60px;width:calc(100% + 120px);height:2px;background:#000;transform:rotate(-5deg);transform-origin:center}.image-placeholder{border:3px dashed #000;border-radius:10px;height:300px;display:flex;align-items:center;justify-content:center;font-size:24px}nav{display:flex;gap:20px;margin-top:20px}nav a{color:#000;text-decoration:none;font-weight:bold}nav a:hover{text-decoration:underline}h1 a{color:#000;text-decoration:none}h1 a:hover{text-decoration:underline}h1{font-size:2.5em;margin-bottom:10px;color:#000}h2{font-size:2em;margin-bottom:20px;color:#000}p{font-size:1.1em;line-height:1.6;color:#000}.content-body,.post,.about-section,.project-section,.more-section{position:relative;padding:40px;margin-left:20px;background:rgba(0,0,0,0)}.section-title{font-size:2.1em;margin-bottom:24px}.post-list{list-style:none;display:flex;flex-direction:column;gap:24px}.post-list-item{position:relative;padding-bottom:20px}.post-list-item::after{content:"";position:absolute;left:0;right:0;bottom:0;height:1px;background:#000;transform:rotate(-1deg)}.post-list-item a{color:#000;text-decoration:none;font-size:1.3em}.post-list-item a:hover{text-decoration:underline}.post-excerpt,.post-content{margin-top:12px}.subtitle{margin-bottom:20px}.miniblog-post{padding-top:28px;padding-bottom:28px}@media (max-width: 768px){.project-section{grid-template-columns:1fr}.content-body,.post,.about-section,.project-section,.more-section{padding:24px}}.decorations .stronk{transform:rotate(6deg);text-align:center}.woah{transform:rotate(-8deg);text-align:middle;vertical-align:top;font-family:Arial,Helvetica,sans-serif}
\ No newline at end of file
+html{background-color:linen}*{margin:0;padding:0;box-sizing:border-box;border-radius:5%;}@font-face{font-family:"Funnel";src:url("fonts/Funnel_Display/static/FunnelDisplay-SemiBold.ttf") format("truetype")}@font-face{font-family:"BoldFunnel";src:url("fonts/Funnel_Display/static/FunnelDisplay-Bold.ttf") format("truetype")}@font-face{font-family:"Dosis";src:url("fonts/Dosis/static/Dosis-Medium.ttf") format("truetype")}h1,h2,h3,h4,h5,h6{font-family:Funnel,Arial}p,a{font-family:Dosis,Arial;margin-top:.5rem;margin-bottom:.5rem}body{min-height:100vh;overflow-x:hidden;background:linen;display:flex}.container{display:flex;width:100%;min-height:100vh;position:relative}.kaomoji-sidebar{position:fixed;top:0;height:100vh;width:60px;font-size:24px;display:flex;align-items:center;justify-content:center;background:linen;overflow:hidden;z-index:100}.kaomoji-sidebar .scroll-text{transform:rotate(.25turn);white-space:nowrap;animation:scroll 90s linear infinite}.kaomoji-left{left:0;border-right:1px solid #000}.kaomoji-right{right:0;border-left:1px solid #000}.kaomoji-right .scroll-text{transform:rotate(.5turn)}@keyframes scroll{0%{transform:rotate(90deg) translateX(-64%)}100%{transform:rotate(90deg) translateX(80%)}}.content{flex:1;margin:0 60px;padding:20px;width:calc(100% - 120px);max-width:1400px;margin-left:auto;margin-right:auto;position:relative}.section-container{position:relative;margin-bottom:40px}.header{position:relative;padding:40px;margin-left:40px;background:rgba(0,0,0,0);margin-bottom:40px}.header h1{font-family:BoldFunnel}.header .nav .prooject{text-size-adjust:.5}.header::after{content:"";position:absolute;bottom:-20px;left:-60px;width:calc(100% + 120px);height:2px;background:#000;transform:rotate(-3deg);transform-origin:center}.about-section{position:relative;padding:40px;background:rgba(0,0,0,0);margin-bottom:40px;margin-left:20px}.project-section{position:relative;padding:40px;background:linen;margin-bottom:40px;margin-left:20px;display:grid;grid-template-columns:1fr 1fr;gap:40px;align-items:center}.project-section::before{content:"";position:absolute;top:-20px;left:-60px;width:calc(100% + 120px);height:2px;background:#000;transform:rotate(3deg);transform-origin:center}.more-section{position:relative;padding:40px;margin-left:20px;background:linen}.more-section::before{content:"";position:absolute;top:-20px;left:-60px;width:calc(100% + 120px);height:2px;background:#000;transform:rotate(-5deg);transform-origin:center}.image-placeholder{border:3px dashed #000;border-radius:10px;height:300px;display:flex;align-items:center;justify-content:center;font-size:24px}nav{display:flex;gap:20px;margin-top:20px}nav a{color:#000;text-decoration:none;font-weight:bold}nav a:hover{text-decoration:underline}h1 a{color:#000;text-decoration:none}h1 a:hover{text-decoration:underline}h1{font-size:2.5em;margin-bottom:10px;color:#000}h2{font-size:2em;margin-bottom:20px;color:#000}p{font-size:1.1em;line-height:1.6;color:#000}.content-body,.post,.about-section,.project-section,.more-section{position:relative;padding:40px;margin-left:20px;background:rgba(0,0,0,0)}.section-title{font-size:2.1em;margin-bottom:24px}.post-list{list-style:none;display:flex;flex-direction:column;gap:24px}.post-list-item{position:relative;padding-bottom:20px}.post-list-item::after{content:"";position:absolute;left:0;right:0;bottom:0;height:1px;background:#000;transform:rotate(-1deg)}.post-list-item a{color:#000;text-decoration:none;font-size:1.3em}.post-list-item a:hover{text-decoration:underline}.post-excerpt,.post-content{margin-top:12px}.subtitle{margin-bottom:20px}.miniblog-post{padding-top:28px;padding-bottom:28px}@media (max-width: 768px){.project-section{grid-template-columns:1fr}.content-body,.post,.about-section,.project-section,.more-section{padding:24px}}.decorations .stronk{transform:rotate(6deg);text-align:center}.woah{transform:rotate(-8deg);text-align:middle;vertical-align:top;font-family:Arial,Helvetica,sans-serif}.giallo{border-radius:5px;padding:1rem}
\ No newline at end of file
index 35b89faa415dbd1c846a30edbf0fa0153f7f94dc..1d1f78ff7a11f58ad8602e451462ffacdedfb51c 100644 (file)
@@ -7,7 +7,7 @@ html {
   padding: 0;
   box-sizing: border-box;
   border-radius: 5%;
-  background-color: linen;
+  /* background-color: linen; */
 }
 
 @font-face {
@@ -37,6 +37,8 @@ h6 {
 p,
 a {
   font-family: Dosis, Arial;
+  margin-top: .5rem;
+  margin-bottom: .5rem;
 }
 
 body {
@@ -335,4 +337,9 @@ p {
   text-align: middle;
   vertical-align: top;
   font-family: Arial, Helvetica, sans-serif;
+}
+
+.giallo {
+  border-radius: 5px;
+  padding: 1rem;
 }
\ No newline at end of file