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:

+
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:

+
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!

+
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:

+
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, 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).

+
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:

+
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:

+
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:

+
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, 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!

+