Kuan

October 19, 2021

Functional Programming in Svelte: Part 1


Getting started without worrying a tiny bit about “Functional Programming”.

You will still be using functional codes, just do not bother with how and why. Follow through, enjoy the readability, expect the result, imagine the potentials and decide at the end.

I wrote this article by assuming not everyone will follow these clone/coding/installing instructions, because, I am not fancy typing anything if I am just here to explore. No guarantee though.

Starting point

Svelte encourages us to always start a web application project from a template repository. To follow this guide, clone the `fp-tailwindcss-svelte-template` svelte-app template repository with degit, like this:

npx degit yuan-kuan/fp-tailwindcss-svelte-template svelte-app
cd svelte-app
npm install

You will need npm and git installed in your system for everything to work. If you do not have these two but is reading this, sure, but, are you sure?

What are we looking at in the base project?

This template project has relatively more source files (.js) to start with. Their names are scary too: free_monad.js, interpreter.js, ref.js, etc. Don’t worry about them now, they are the framework’s source files. Feel free to study them if you want, just, be brave.

main.js looks similar to any other Svelte project’s main.js except this line: `addSop(() => viewMainPage(Sample))`. We will look at this again later.

App.svelte, on the other hand, looks daunting even for a Svelte veteran. This is not where you will find the “entry point”. This is uncommon in a Svelte project, and it isn’t the only uncommon pattern you will encounter.

Lastly, there are a few extra files that are related to TailwindCSS.

In Part 1, we will be mostly working in main.js and create a new Svelte component (*.svelte).

Hello World

To start developing this project, run `npm run dev` in the command line. There should be an instruction that asks you to visit `http://localhost:5000` when it succeded. Do that, and you shall see a simple web page with a simple message displayed on it.

Unfortunately, it does not say “Hello World”. Blaspmerry! Let us right the wrong.

  1. Create a new file and name it `Hello.svelte`. You can place it anywhere you like, I’d suggest putting it in /src/view for now.
  2. Type your “Hello World” in it, be fancy. Or copy mine:
  3. <p class="w-full pt-3 font-light text-5xl text-center underline">Hello World</p>
  4. Open up `main.js`, replace the Sample’s import with our new Hello’s:
  5. // REMOVE THIS LINE
    import Sample from './view/Sample.svelte';
    
    // ADD THIS LINE
    import Hello from './view/Hello.svelte';
  6. Next, modify the funny looking `addSop` line to this:
  7. // Change "Sample" to "Hello"
    addSop(() => viewMainPage(Hello));
  8. Save the file and check out the web page again.

You probably have a few questions but I am not going to answer them now. Stay with me though!

Hello {$name}

Now, let us crank things up a bit by adding some reactivity. After all, what’s the point of using Svelte without leveraging its reactiveness?

In fp-svelte, we will be relying on Svelte Store for all the reactivity, i.e. dynamically changing the DOM. Do the following changes:

  1. Open up Hello.svelte, add the following codes to the top:
  2. <script context="module">
    import {createRef} from '../ref.js';
    
    export const name = createRef();
    </script>
  3. Change the hello world to `Hello {$name}`. {$store} is a Svelte syntax to update the DOM whenever the name Store has a new value.
  4. Open up `main.js` now, replace the `addSop(() => viewMainPage(Hello));` line with these:
  5. addSop(() =>
      free.sequence([
        viewMainPage(Hello),
        setRef(name, 'Svelte in FP')])
    );
  6. Import the two new dependencies at the top of `main.js` to make the codes compiled:
  7. import * as free from './free_monad';
    import { setRef } from './ref';
    import Hello, { name } from './view/Hello.svelte';
  8. Save all these changes, and observe the new page in the browser.

If you made it past step 3, bravo! If you are mildly curious now, I thank you for all the patient to keep up with the journey. I owe you a lot of answers. But I will not discuss them in this article. They are worth a whole new article, which can be a bit philosophical, and I will be writing it next.

We are not done here. I will be amiss if I failed to give a small glimpse of the user interactivity function in this small guide. Last exercise.

Hello <value-from-textinput>

Lets us add a text input and button to the Hello page. The goal here is to let users key in their name in the text input, press the button and the page will say “Hello ”. Easy!

  1. Open up `Hello.svelte`, add the following HTML to the template section:
  2. <div class="flex justify-center p-4">
      <input
        class="appearance-none bg-gray-200 text-gray-700 border rounded py-2 px-4 leading-tight "
        type="text"
        placeholder="new name here"
        bind:value={username}
      />
      <button
        class="bg-blue-500 text-white font-bold py-2 px-4 rounded"
        on:click={confirm}>Confirm</button
      >
    </div>
  3. Add a new <script> section with the following new variable and new function
  4. <script>
    let username;
    
    const confirm = () => {
      $changeName(username);
      username = '';
    };
    </script>
  5. Woah, what is that? Hang in there. It is a closure function as Svelte Store. Which we will define it in our `<script context="module">` section:
  6. <script context="module">
    import {createRef} from '../ref.js';
    
    export const name = createRef();
    export const changeName = createRef();
    </script>

Let us take a break here and consider the alternative: just set the input’s value into $name! Correct, but this is not a Svelte demo. I like to show you how it could be done in a more functional programming way.

We are done with the template. Now it is time to write more functional programming codes:

  1. Open main.js, add the changeName import:
  2. import Hello, { name, changeName } from './view/Hello.svelte';
  3. Add a new function. This function would be called every time the user clicks on the new button we just added recently. To prove that we have done more works than just setting the value, we will capitalize the input’s value:
  4. const performChangeName = (newName) =>
      free.of(newName)
        .map(R.toUpper)
        .chain(setRef(name));
  5. We just introduced Ramda. Import it:
  6. import * as R from 'ramda';
  7. Finally, we need to set up the closure function, which will be called via the $changeName Svelte Store. Edit the addSop lines to these
  8. addSop(() =>
      free.sequence([
        viewMainPage(Hello),
        setRef(name, 'Svelte in FP'),
        setRef(changeName, (newValue) => addSop(() => performChangeName(newValue)))
      ])
    );
  9. Save and check out the new local site. Test out the new functionality.

The last line can be cryptic even for a seasonal JavaScript programmer. It means assigning a closure function, which shall be invoked with a `newValue` argument, to the `changeName` Svelte Store. This closure function consists of a single instruction: `addSop(() => performChangeName(newValue))`.

There are a lot more I like to talk about addSop, but this takes a whole new article for it. Now, just think of it like this: queue up a closure function with the system. This closure will be trigger when the system decides it is time to run it. Running “SOP” is our way to keep this web application responsive.

End of Part 1

This is an opinionated way to develop a web application. I’ve demonstrated taking extra turns to accomplish a simple little task. As if I’d fired up a chainsaw to chop a carrot. There are reasons for these, and they all boil down to having a complete functional architecture web application.

You can study the complete source code of this at https://github.com/yuan-kuan/blog-fp-svelte-part-1.

If you are already interested or want to see if this hold in the real world, check this out: Zasa. This is a PWA build on top of this very architecture.

If you like to keep track of this series of tutorials, subscribe! Or if you have any comments, please reach me at kuan@hey.com.

About Kuan

Web developer building with Flutter, Svelte and JavaScript. Recently fell in love with functional programming.

Malaysian. Proud Sabahan. Ex game developer but still like playing games.

New found hobby is outdoor camping with my love.