DEV Community

Cover image for DevLog 00001 - Command Line Updates
Nick Tchayka for NeoHaskell

Posted on • Updated on

DevLog 00001 - Command Line Updates

It was a long time since I wrote something decent about NeoHaskell, and my plan was to build openly, so I might start writing devlogs more frequently to publish progress and be able to receive feedback. Many people have sent suggestions about the project and I’m very grateful about them. Also, I think it is cool to have some logs to see how the stuff progresses. It’s easy to dismiss work when one doesn’t write about it.

Lately I’ve been pushing NeoHaskell to get the minimal necessary functionality to be able to write a command line tool for interacting with NeoHaskell projects. This led me to implement multiple micro features in the project, but also it started shaping some interesting modules in the core library.

Perhaps one of the greatest finds for the core library was the awesome library opt-env-conf by NorfairKing on GitHub. What looks on the surface as just another command line argument parsing library, is actually a masterpiece in developer experience design IMO. This library not only allows you to define parsers for your command line program arguments, but also define requirements in terms of environment variables and configuration files. On top of that, it also lets you generate manpages, and autocompletions for your typical shells like Bash, ZSH, Fish, and others.

This library alings very well with the NeoHaskell philosophy of “let the tool do as much as possible, freeing the user of repetitive tasks”. I’ve validated the existence of environment variables countless times in many languages and with many different tools in techniques. It is very refreshing to be able to have a library that has this in mind already, and that’s why I’ve chosen it as the underlying technology of the OptionsParser module in the NeoHaskell core library (name probably will change).

One of the main patterns in NeoHaskell is the Parse, don’t validate philosophy. But NeoHaskell tries to take it even further, by allowing the developer to write parsing code once.

How? Right now it is not fully decided yet, since we have only one parsing module (OptionsParser), but the idea is that you write parsing code once with the parser of your choice (e.g. OptionsParser), and then, if needed, you could switch into a different one (e.g. JSON) with minimal code changes, allowing your app code to evolve slowly into different implementations and platorms.

Perhaps you start writing a CLI app that’s easy to test, but then you decide that it should be an HTTP server, after a while you decide that it could be a mobile app. Those switches shouldn’t be a whole rewrite of the app, and the parsing code for the types that make the boundaries of your application should stay the same, or at least as similar as possible to the previous iteration.

The OptionsParser module implements a thin layer over opt-env-conf to start defining this kind of API, where one can parse different fields by providing a configuration in a record.

Records

Another great find that goes straight into the style of NeoHaskell is large-anon. A Library that allows working with records in a very flexible way.

Of course, Haskell does have records through the usage of data, but those aren’t expandable, nor we can match structurally in type signatures easily. And of course, we cannot just make a record on the fly and pass it to a function.

large-anon provides fixes to all these problems, and also provides a cool DSL that allows you to write code to build records in an imperative way. Also, they implement JSON serialization out of the box, so you don’t have to write any code to enable it for the records you write.

In Haskell, these constructs aren’t first class, so in future versions of NeoHaskell, custom syntax will be introduced to work easily with records.

Commands: Side effects and interop

By the time I’m writing this, I’m in the middle of implementing commands and command handling.

Commands in NeoHaskell are very similar to Cmds in Elm. It is a value that, if picked by the NeoHaskell runtime, it will execute some kind of side effect.

There’s an interesting twist in NeoHaskell commands: The command definition and the handling is completely decoupled.

In fact, not only the handling is done in a separate thread, but you could implement handlers in any language. Yes, you read that right.

Of course, right now this would be pretty rudimentary, but over the versions I hope that the API stabilizes and one could have a very lightweight library for different languages to implement these command handlers in a very easy way that’s integrated with the rest of the codebase.

On top of that, commands can be declared idempotent, this means that we say that the command can be repeated without additional effects happening. Think of when you call an elevator and you keep pushing the button, no additional effects happen, the elevator keeps coming.

This is useful for when the time travelling debugger gets implemented. The idea is that you should be able to debug your app by going forward and backward through the execution of the code.

Event Sourcing and the Elm architecture

The time travelling thingy would be possible thanks to the fact that the main architecture for NeoHaskell apps is the Elm architecture, which essentially is event sourcing

If you’re not familiar with either one, think of your bank account. When you go into the ledger of it, you can see the withdrawals and deposits. And thanks to those, you can calculate the balance at any time by looking at those.

That’s how event sourcing works essentially, you store events and you calculate the state of the app out of them.

What next?

Once I finish implementing the necessary things to have a hello world app for the terminal using these patterns, I’ll probably start to work on a build command in the neo CLI tool that grabs project.yaml and builds the project from there.

I’ve got lots of ideas to come for that, like a nice terminal UI based on minttea by leostera. Also, implicit imports that are configurable, so you don’t have to import any module if you already have it in your deps, and start implementing custom syntax.

I’m pretty excited about the project, it’s addictive to code on this codebase heh.

As always, I invite you to the Discord server to have a chat, and of course to the GitHub repo to contribute if that’s your thing. See you around!

Top comments (0)