CLI application with Functional Programming using fpdart

Sandro Maglione

Sandro Maglione

Functional programming

Have you ever wondered how a CLI app is implemented?

Out there is not all frontend or backend, mobile or web. Tooling is also a thing. A major one 💁🏼‍♂️

This week we use Functional Programming with fpdart to implement our own CLI.

Here is how it works 👇


Tech stack

  • Dart: the app is written in plain dart, no framework or anything else needed
  • fpdart: the app uses fpdart for Functional Programming, which makes it safe, readable, and super convenient when it comes to error handling

Setup

Since the app is plain dart you can just execute dart create. We use the console template:

dart create -t console <name>

This creates a simple setup:

  • lib: where we are going to implement the CLI
  • bin: contains the main function, entry point where the CLI is executed
  • test

Get started

A CLI (Command-Line Interface) allows to run a command on the terminal and execute some code. As simple as that.

You can do things like:

  • Read and write files
  • Make http requests
  • Scan files and apply formatting or linting
  • Create a project from a template

We are going to implement a CLI that scans all the folders and dart files and finds all unused files (no imports)

This requires the following steps:

  • Read all the files
  • Extract imports
  • Find which files are unused
  • Report the result

Dart provides many resources to get started:

There is more.

Every week I build a new open source project, with a new language or library, and teach you how I did it, what I learned, and how you can do the same. Join me and other 600+ readers.

Implementation

The main function in dart accepts a List<String> parameter (arguments):

arguments represents the arguments provided when running the command on the terminal.

The args package allows to easily parse arguments and extract flags and options. The app accepts a -o option to specify the location of the configuration file:

dart run bin/dart_cli_with_fpdart.dart -o cli_options.yaml

The implementation is based on 3 principles:

  • Pattern matching with sealed class for error handling: this allows to define all errors in a single file and make sure to handle them all using patterns
  • Dependency injection: all the modules are defined as abstract classes, and then implemented and injected
  • Functional programming: the implementation relies on fpdart to compose modules, handle errors, and execute sync and async operations

The resulting main function is safe, easy to read and maintain:

void main(List<String> arguments) async =>
    program(arguments) /// Execute the `program` [ReaderTaskEither]
      .match<void>((cliError) {
        exitCode = 2;
 
        /// Handle [CliError] here ⛔️
      }, (result) {
        exitCode = 0;
 
        /// Report unused files here ✅
      }).run(
        /// Provide all dependencies (Dependency injection) 💉
        AppMainLayer(
          argumentsParser: ArgumentsParserImpl(ArgParser()),
          configReader: ConfigReaderImpl(YamlLoaderImpl()),
          fileReader: FileReaderImpl(),
        ),
      );

👉 For all the details and code snippets you can read the full article containing all the details of the implementation.

Example of running the final CLI app: all the used and unused files are reported. You can go ahead and remove all used files!Example of running the final CLI app: all the used and unused files are reported. You can go ahead and remove all used files!

Takeaways

  • I would recommend considering dart for CLI development, the setup is super easy and the tools work great
  • Combining pattern matching, fpdart, and dependency injection makes everything safe and easy to read and maintain
  • CLI development allows to focus 100% on the implementation: no need to handle multiple screen sizes, no UI, no versioning. Just plain dart. This makes it great if your focus is learning the language without worrying about any extra configuration
  • It's easier than you think to end up with unused file 😅

I strongly suggest you to explore and try implementing your own CLI app. It's definitely a great way to learn any language.

That's it for this week project. Looking forward to start another one for next week!

See you next 👋

Start here.

Every week I build a new open source project, with a new language or library, and teach you how I did it, what I learned, and how you can do the same. Join me and other 600+ readers.