Blue zen garden stones and raked sand

ライフ ・ バランス

code flows like water balance found in empty lines the terminal waits

Featured Posts

BakaArts

Functional programming in TypeScript

Part 1 - Introduction and Function Composition

Functional programming is a programming paradigm, which allows us to write our programs in a declarative and composable way by combining functions.

Functional programming in TypeScript?

Functional programming is language agnostic, meaning that we can apply it using almost any language. The only requirement is that the language treats functions as first-class citizens, which means that we can:

  • Assign functions to variables.
  • Pass functions as arguments to other functions.
  • Use functions as return values from other functions.

All of the above is doable in TypeScript, which makes it a suitable choice to write code in the FP paradigm.

NOTE

Pure functional programming is a somehow more strict paradigm, and the exact difference between pure and impure functional programming is a matter of controversy. In fact, the earliest programming languages cited as being functional, IPL and Lisp, are both impure functional languages. Some examples of pure functional languages are Haskell and PureScript.

There are several libraries such as ramda or fp-ts, that include features to allow us to write in a more pure functional style. In this series of articles, we won’t be using any of these, just vanilla TS.

But what is Functional Programming about?

FP is about taking some input, and process it by using a chain of transformations (each transformations is accomplished by a function). When each of these functions is called with some given arguments, it will always return the same result (pure function).

In constrast, an impure function, like the ones we use in imperative programming, can have side effects (such as modifying the program’s state or taking input from a user). Side effects are considered undesirable in pure functional programming because they make functions less predictable and harder to test.

NOTE

But at some point, our application will have to interact with the outside world (get user input), how does FP deal with that? FP relegates dealing with side effects to a thin layer outside our application.

Some defining features of FP are:

  • Functions are at the center of the paradigm. We end up with a bunch of reusable units of code, which leads to having to write less code to achive the same results.
  • These functions are quite deterministic, meaning, given a given input, they always return the same output, regardless of the program’s state.
  • Easy to test code: As a result, when writing tests for the functions, we don’t have to worry about program’s state.
  • Easier to debug: FP aims to minimize or isolate side effects to make code more predictable, testable, and easier to debug.
  • We’ll see that for or while loops are not used in FP; in these scenarios we use recursion.

As with any other approach, it will take time to become comfortable using it. As a result, noticing its benefits will take some time.

Pure Functions

A pure function has the following characteristics:

  • It’s deterministic, meaning that given the same input (given in the arguments), the function always returns the same output. That implies that the function shouldn’t be affected by the state of the program (no side causes), only by its input.
  • A pure function has no side effects, meaning that calling such a function should not mutate the state of the program.
Pipe analogy

In FP, we don’t use variables only constants. Each transformation (function call) returns a new value, which is assigned to a constant. This constant can be used as input (argument) in the next transformation.

Total vs Partial Functions

A total function is a function that is defined for all possible inputs in its domain. This means that for every input value in the domain, the function will produce a valid output. For example:

function add(a: number, b: number): number {
return a + b
}

A partial function is a function that is defined for only a subset of inputs in its domain. For some inputs, the function may not produce a valid output or may result in an error. For example:

function divide(a: number, b: number): number {
if (b === 0) {
throw new Error('Division by zero is not allowed')
}
return a / b
}

So the main difference is that total functions map all of its inputs to some output value, whereas partial functions map only a subset of its inputs to some output:

Total vs partial functions

IMPORTANT

Deterministic also means that a given input value can be mapped to one and only one output value. If an input value could result in one of several output values, that’s not very deterministic, ain’t it?

Key Differences Between Total and Partial Functions

AspectTotal FunctionPartial Function
DefinitionDefined for all inputs in its domain.Defined for only a subset of inputs.
BehaviorAlways produces a valid output.May fail or throw errors for some inputs.
Input ValidationNo input validation is needed.Input validation is often required.

Why Total Functions Are Preferred in Functional Programming

  1. Predictability: Total functions are easier to reason about because they are guaranteed to work for all inputs.

  2. No Runtime Errors: Total functions eliminate the risk of runtime errors caused by invalid inputs.

  3. Composability: Total functions can be composed more easily because they always produce valid outputs, which can be passed as inputs to other functions.

  4. Immutability: Total functions align with the principles of functional programming, where functions are deterministic and side-effect-free.

How to Handle Partial Functions in FP

In functional programming, partial functions are often avoided or handled explicitly using techniques like:

  1. Option Types (e.g., Maybe or Option): Wrap the result in a type that explicitly represents the possibility of failure. For example:
function safeDivide(a: number, b: number): number | null {
return b === 0 ? null : a / b
}
  1. Validation: Validate inputs before calling the function to ensure they are within the valid subset.
function divide(a: number, b: number): number {
if (b === 0) {
throw new Error('Division by zero is not allowed')
}
return a / b
}

Function Composition

Most of what we do in FP is composing functions, which consists in combining two or more functions to produce a new function; The output of one function becomes the input of the next one. Function composition allows us to build complex operations by combining smaller, reusable functions.

For example, imagine we have the function increment:

type Increment = (x: number) => number
const increment = (x: number): number => x + 1

And also the function stringify:

type Stringify = (x: number) => string
const stringify: Stringify = (x) => x.toString()

Using function composition we can define a higher-order function named incrementAndStringify, where we connect the output tof increment to the input of stringify:

type IncrementAndStringify = (x: number) => string
const incrementAndStringify: IncrementAndStringify = (x) => stringify(increment(x))
const result: string = incrementAndStringify(1)
console.log(result) // "2"

NOTE

Function composition is closely related to the property of referential transparency. A piece of code is referentially transparent if you can replace a function call with its result without changing the behavior of the program. For example:

const stringify = (x: number): string => x.toString()
const add = (a: number, b: number): number => a + b
const result = stringify(add(2, 3))
const result2 = stringify(5) // We can replace add(2, 3) with 5

A Step Up

In the previous section, we were composing functions by hardcoding their names into the composed version. It would be nicer if we could create a third function, which takes two functions, and return their composition:

type Increment = (x: number) => number
const increment: Increment = (x) => x + 1
type Stringify = (x: number) => string
const stringify: Stringify = (x) => x.toString()
type Compose = (
f: (x: number) => string,
g: (x: number) => number
) => (x: number) => string
const compose: Compose = (f, g) => x => f(g(x))
const incrementAndStringify = compose(stringify, increment)
console.log(incrementAndStringify(33)) // "34"

Adding Generic Types

It would be even nicer, if we could define our compose function so that it’s not limited to the types string and number. Check this out:

type Increment = (x: number) => number
const increment: Increment = (x) => x + 1
type Stringify = (x: number) => string
const stringify: Stringify = (x) => x.toString()
type IncrementAndStringify = (x: number) => string
type Compose = <T, U>(
f: (x: T) => U,
g: (x: T) => T
) => (x: T) => U
const compose: Compose = (f, g) => x => f(g(x))
const incrementAndStringify: IncrementAndStringify = compose(stringify, increment)
console.log(incrementAndStringify(41)) // "42"

In the code above we are using two generics that allow us to replace T and U by any types. That way we can compose any two functions with different type signatures. In the way we defined the compose function above, we have only two constraints:

  • The type of input of g, f, and our compose function is the same.
  • The type of output of f and our compose function is the same.

Using function composition allows us to reuse a lot of small functions by composing them in different ways, to achieve different results. Testing each of these functions is easy.

A Generic compose Function

We could take it a step further, and define a super generic compose function, that could take any two functions, no matter their signature:

type Compose = <A, B, C>(
f: (x: B) => C,
g: (x: A) => B
) => (x: A) => C
const compose: Compose = (f, g) => x => f(g(x))
const incrementAndStringify: IncrementAndStringify = compose(stringify, increment)
console.log(incrementAndStringify(41)) // "42"

Here, we define a generic compose function compose with three generic types:

  • A: The type of input to g (the first function in the composition), and to the compose function itself.
  • B: The type of input to f, and the output from g.
  • C: The type of output from f, and the output of compose (the final result).
Bad coffee

React Router 7

How to use the Framework mode

React Router is a multi-strategy router for React, which means it can be used in different ways depending on the needs of your application. In this guide, we’ll explore the framework mode, which is a new way to use React Router that allows you to define your routes in a more declarative way.

Generating a new React project

The framework mode is the one that offers the most features (check comparison table), hence it has a few dependencies than the other modes. For that reason, setting it up manually it’s a bit tricky. A better idea is to generate a new project using the following command:

Terminal window
npx create-react-router@latest my-react-router-app

Where my-react-router-app is the name of our project. This command will generate a new React project with the all the necessary dependencies to use React Router in framework mode.

NOTE

Later in this guide, and as an exercise, we’ll see how to add the framework mode to an already existing React project.

Once we have our project created, we can navigate to the root folder and start the development server:

Terminal window
cd my-react-router-app
npm run dev

Important Files

If we open the project in our code editor, we should see a bunch of files and folders, but the most important ones are inside the app folder:

  • app/root.tsx: This is the entry point of our application, from where we export:
    • The Route.LinksFunction.
    • The Layout component.
    • The App component.
    • An ErrorBoundary component.
  • app/routes.ts: This is where we define our routes.

Defining Routes

Routes must be defined in the app/routes.ts file. For example, let’s define a simple route that renders a route module named home.tsx when the URL is /:

app/routes.ts
import { type RouteConfig, index } from '@react-router/dev/routes'
export default [
route('/', 'routes/home.tsx')
] satisfies RouteConfig

The route function is known as a route matcher; this route matcher takes two arguments:

  • A URL pattern to match the URL, in this case ’/’.
  • A file path to the route module that will be rendered when the URL matches the pattern.

Since having a route for the home page is a common use case, React Router provides a helper function called index that does the same as route('/', 'routes/home.tsx'):

app/routes.ts
import { type RouteConfig, index } from '@react-router/dev/routes'
export default [
route('/', 'routes/home.tsx')
index('routes/home.tsx')
] satisfies RouteConfig

These are the contents of routes/home.tsx:

routes/home.tsx
export default function Index() {
return <h1>You are at Index</h1>
}

IMPORTANT

Note that routes/home.tsx is a route module and not just a React component. We have to export the React component as default, otherwise we’ll get error:

You made a GET request to / but did not provide a `loader` for route "routes/home", so there is no way to handle the request.

Yeah, the error is a bit misleading, because we’re not loading any data in our componet; again, it happens because we didn’t export the component as default.

Adding a Loader

Let’s define data loading for our route module. There are two ways to do this:

Client Data Loading

Let’s add a clientLoader function to fetch a list of TODOs from the JSONPlaceholder API. We’ll render the list of TODOs in the home.tsx route module:

routes/home.tsx
import type { Route } from '../+types/root'
interface Todo {
userId: number
id: number
title: string
completed: boolean
} // Better move this type to a separate file (types/todo.ts) and import it here.
export async function clientLoader() {
const response = await fetch('https://jsonplaceholder.typicode.com/todos')
if (!response.ok) {
throw new Error('Failed to fetch todos')
}
return response.json()
}
export default function Home({ loaderData }: Route.ComponentProps) {
return (
<main>
<h1>You are at Index</h1>
<h2>Todos</h2>
<ul>
{(loaderData ?? []).map((todo: Todo) => (
<li key={todo.id} className="py-4">
<h3>{todo.title}</h3>
<p>{todo.completed ? 'Completed' : 'Not Completed'}</p>
</li>
))}
</ul>
</main>
)
}

Yeah, I know, it’s a lot of code for rendering just a TODO list!, but let’s break it down:

  • We define a Todo type that represents the structure of the data we’re going to fetch.
  • Note that clientLoader runs on the client side, like a traditional SPA; open the network tab to verify the request.
  • In the React component, we destructure the loaderData prop to access the data fetched by the clientLoader function. Then we render the list of TODOs.

TIP

When testing this code, the browser’s console will show a recommendation to use the HydrateFallback, so I added:

// HydrateFallback is rendered while the client loader is running
export function HydrateFallback() {
return <div>Loading...</div>;
}

Nice! So no need of defining React loading states.

Server Data Loading

Server Data Loading works the same way as the clientLoader, but it runs on the server side. Let’s add a loader function to fetch the individual TODOs:

routes/todo.tsx
import type { Route } from '../+types/root'
import { Link } from 'react-router'
import type { Todo } from 'types/todo'
export async function loader({ params }: { params: { id: string } }) {
const { id } = params
const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${id}`)
if (!response.ok) {
throw new Error('Failed to fetch todos!')
}
return await response.json()
}
export default function Todo({ loaderData }: Route.ComponentProps) {
const todo = loaderData ? (loaderData as Todo) : null
return (
<main>
<Link to="/">Back Home</Link>
{todo && (
<div key={todo.id}>
<h3>{todo.title}</h3>
<p>{todo.completed ? 'Completed' : 'Not Completed'}</p>
</div>
)}
</main>
)
}

TIP

Feel free to wrap the TODOs in routes/home.tsx in a Link component to navigate to the individual TODO route with a click.

The loader function runs on the server, so you won’t be able to see the network request in the browser’s console. We get good old HTML from the backend, which you can verify it by disabling JavaScript in the browser.

IMPORTANT

Remember to add a route in app/routes.ts to render the todo.tsx route module:

app/routes.ts
import { type RouteConfig, index, route } from '@react-router/dev/routes'
export default [
index('routes/home.tsx'),
route('todo/:id', 'routes/todo.tsx')
] satisfies RouteConfig

In the code above, we’re using a dynamic segment in the first argument of the route function (todo:id). We end up with a dynamic route matcher that matches the URL /todo/:id and renders the todo.tsx route module. The :id part is an argument that we’ll be used in the loader function to render the TODO with that id.

Adding a Layout Route

It’s pretty common to have a layout component shared amongst several routes in our application. As you can see, both our route modules have a main element repeated (in real life scenarios we’d have way more than that). We can define a layout route in the app/root.tsx file:

app/root.tsx
import { Layout } from '@react-router/dev/routes'
export default [
layout("./components/layout.tsx", [
index('routes/home.tsx'),
route('todo/:id', 'routes/todo.tsx')
]),
]

An this is the content of components/layout.tsx:

components/layout.tsx
import { Outlet } from 'react-router'
export default function Layout() {
return (
<div>
<header>
<h1>TODO list app</h1>
</header>
<main>
<Outlet />
</main>
</div>
)
}

So the way this works is that the Layout component is rendered for all the nested routes, wrapping them, and the Outlet component is used to render the child routes.

IMPORTANT

Every route in routes.ts is nested inside the special root route, defined in the app/root.tsx module (Remember the Layout component we mentioned at the beginning?).

Nested routes

We can also define nested routes in our application. Let’s say we want to have a

Adding a 404 Route

This one is really simple, we just have to add a route matcher that matches any URL:

app/routes.ts
import { type RouteConfig, index, route } from '@react-router/dev/routes'
export default [
index('routes/home.tsx'),
route('todo/:id', 'routes/todo.tsx'),
route('*', 'routes/not-found.tsx')
] satisfies RouteConfig

And the content of routes/not-found.tsx:

routes/not-found.tsx
export default function NotFound() {
return <h1>404 - Not Found</h1>
}
Cool Pic

Deploying your Astro blog

Keep your Astro source private and publish only the static build

GitHub Pages for private respositories is not supported on GitHub Free (read here) and I didn’t like the idea of making the whole blog’s source code and post drafts publicly available. So I had to come up with a solution.

NOTE

I’m aware that Cloudfare Pages it’s free, faster than GitHub Pages, and supports private repos. But I also had the comments section tied to the public repo so. Maybe next time…

The idea is simply using a two-repository strategy:

  1. Private repo - Where we keep our source code with drafts, work-in-progress posts, and all our Astro source files.
  2. Public repo - Just the contents of the dist/ folder, for GitHub Pages deployment.

Configuring the Deployment Target

By deployment target, I mean the public repo from where we will will serve the site to the world, using GitHub Pages.

NOTE

Remember, our private repo is where we write posts, keep drafts, and build the Astro project. The public repo is just where we push the final static output.

In our public repo, we have to configure a publishing source for GitHub Pages. There are a couple of options here:

  • To publish when changes are pushed to a specific branch.
  • Or you can write a GitHub Actions workflow to publish your site.

We’ll be using the first option; just click on the Settings tab of your repo, and once there, find the Pages slot in the left sidebar. Mine looked like this:

the branch method

What this means is that whenever we push changes to the master branch (root folder /), our GitHub pages will be publish.

For Astro Users

Before building our site, we need to tell Astro the final public URL where our site will be published. This is controlled by two settings in astro.config.ts:

  • site is the domain where we will serve the site.
  • base is the path under that domain where your site will live.

Since we will be serving the page from the URL of the public repo, we need to add it to our astro.config.ts:

export default defineConfig({
site: 'https://<username>.github.io',
base: '/',
// more stuff...
})

If your public repo is the special GitHub Pages repo named <username>.github.io, then the site is served from the root: https://<username>.github.io/.

TIP

In my case, since it was my personal page, I used the https://<username>.github.io URL. But it doesn’t have to be, any public repo URL will do, e.g. https://github.com/<username>/<repo-name>.

But if you’re publishing to a repo named foo, then you should use something like:

  • site: 'https://github.com'
  • base: 'foo'

TLDR

Just three steps:

  1. Build your project; from the private repo run:
Terminal window
npm run build
  1. Copy the contents of the dist folder, from the private repo to the public repo:
Terminal window
rsync -a --delete --exclude '.git' dist/ ../my-blog-public/
  1. Move to the public repo, and push changes:
Terminal window
cd ../my-blog-public
git add -A
git commit -m 'new build'
git push

1. Creating the Build

Not much to say here, whenever you add a new post, and are ready to publish, you need to build right? Let’s assume you have the following build script in the package.json of your private repo:

"build": "astro build"

We just have to change to the root of your private repo, and run:

Terminal window
npm run build

In the case of an Astro project, we should end up with a dist folder containing the artifacts of our build.

2. Copy the Build to the Public Repo

Now the goal is to move the contents of the dist folder (not the folder itself, but its contents), to the root of our public repo. :

Terminal window
rsync -a --delete --exclude '.git' dist/ ../my-blog-public/

Let’s go over the flags:

  • dist/ with trailing slash copies the contents of dist, not the folder itself.
  • --delete removes stale files from the public repo.
  • --exclude '.git' preserves the public repo Git metadata.
  • It copies hidden files like .nojekyll.

TIP

In Astro, dist is the default build folder; but if you’re using another one, or not even using Astro, feel free to use whatever folder you need to.

The command above assumes that you have cloned your public repo to the parent folder of the private repo. Check the following folder structure:

Terminal window
parent-folder/
├── my-private-repo/ # 👈 We're working here.
├── src/
├── content/
└── dist/
└── my-blog-public/ # 👈 Make sure you have cloned your public repo.
└── .git/

3. Pushing Changes to the Public Repo

Push the changes to your GitHub public remote:

Terminal window
cd ../my-blog-public
git add -A # Stage the changes
git commit -m "New build" # Commit
git push # Push

That should be enough you have your page live after a few seconds.

The future

uv - Blazingly Fast 😏 🚀

A Python's Package and Project Manager written in Rust

uv is an extremely fast package and project manager for Python, written in Rust. I hear you mumbling, another package manager for Python? Bro, this time is different, hear me out, uv really is the future. In this post we’ll spend some minutes learning how to use it.

What can I do with it?

Lots of things, but from the top of my head:

Installation

There are plenty of ways to get this puppy up and running in our machine. The first one, if you’re lucky enough to be on Linux or macOS:

Terminal window
$ curl -LsSf https://astral.sh/uv/install.sh | sh

After the installation, we have to restart our shell (just type exit in your terminal). That’s because the installer added uv to our PATH.

NOTE

If you’re in Windows 🤢:

Terminal window
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

For Christ’s sake, you can even install it with pip!

Terminal window
pip install uv

Anyways, once you’ve installed it, and as a sanity test, run:

Terminal window
$ uv
An extremely fast Python package manager.
Usage: uv [OPTIONS] <COMMAND>

TIP

Check the docs for uninstallation if you need to.

Installing Python Versions

uv can also install and manage Python versions:

Terminal window
$ uv python install

That would get us the latest version, but if you want to install a specific one, just say it:

Terminal window
$ uv python install 3.12

NOTE

If you run the command above (regardless if you’re in a project folder), it will install Python in ~/.local/share/uv/python/cpython-3.13.3-macos-aar, and create a symlink to it in ~/.local/bin/python3.13.

To see a list of the available and installed Python versions:

Terminal window
$ uv python list

IMPORTANT

We don’t need to install Python to get started; If Python is already installed on your system, uv will detect and use it.

Projects

uv supports managing Python projects, which define their dependencies in a pyproject.toml file. Like in other languages, we can create a new Python project using the uv init command:

Terminal window
uv init my-proj

That will create the following project structure within the my-proj folder:

my-proj/
.
├── .python-version
├── README.md
├── main.py
└── pyproject.toml

TIP

If you already have a project folder, let’s say you already have the my-proj folder, you can run just uv init within the folder.

The main.py file contains a simple “Hello world” program. Try it out with uv run:

Terminal window
$ cd my-proj
$ uv run main.py
Using CPython 3.13.2 interpreter at: /usr/bin/python3.13
Creating virtual environment at: .venv
Hello from my-proj!

Now, if we list the content of our project:

Terminal window
$ ls -a
.
├── .git
├── .gitignore
├── .venv
│   ├── bin
│   ├── lib
│   └── pyvenv.cfg
├── .python-version
├── README.md
├── main.py
├── pyproject.toml
└── uv.lock

That’s right, uv has created a virtual environment (in .venv folder), and initialized a git repo for us. From a project point of view, the pyproject.toml contains our project’s metadata (sort of the package.json in Node.js):

[project]
name = "hello-world"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
dependencies = []

Managing Dependencies

Managing dependencies is easy. Let’s say we want to install marimo:

uv add marimo

Marimo includes a tutorial, so let’s run it, to verify it’s installed:

uv run marimo tutorial intro

WARNING

If you try to run:

marimo tutorial
zsh: command not found: marimo

Your shell will complain! That’s because when we run uv add marimo, uv installs marimo into a virtual environment (under the .venv folder). This environment is isolated, meaning the marimo executable is only available when the virtual environment is activated or when you explicitly use uv run to access it.

I hope that was enough to get you started; feel free to check the official docs, they’re awesome.

Featured Projects

Zentry

Zentry Clone

A cool landing page

This time I decided to build a landing page that closely follows zentry. Doing this project I realize that having good graphic assets can push your design quite far, with less effort.

See it live here 👈

Used Technologies

Light theme Saas

Progress tracker

A light themed landing page

A landing page about a fictitious Saas, where I used responsive design (mobile, tablet and desktop) in a light theme. I added some parallax effects and attractive animations.

See it live here 👈

Used Technologies