I said in Haskell setup that posts like these don’t age well. And sure enough, here I am with a revision. After dipping my toes in the Haskell testing waters I wanted a project scaffolding that is different from what cabal init generates:

  • I wanted a proper testing setup that allows me to also test module internals.
  • I also wanted to solve having saved snippets of bindings loaded into the repl so I don’t have to repeatedly type the same expressions.

After a bunch of trials and errors I settled on the project structure as seen in magicsquare. It breaks out internals into a separate module so it can be tested. It defines an Examples module that imports the other two modules and defines snippets useful in the repl. I invoke the repl like so:

cabal repl examples

All the functions from public and internal modules are available in the repl in addition to everything Examples defines.

That’s all cool and I’m happy how the magicsquare project is set up. But what about a new project ? Do I cabal init something, answer the questions and then retrofit the generated scaffolding into this new structure ? Or do I copy magicsquare and rename and delete stuff ?

I didn’t like any of these two options, so instead I whipped up a small utility There are probably tons of similar tools but it was easier for me to spit one out using Go than to find a fitting, already written tool. And yes, I wrote it in Go. It’s hard to write it in Haskell when you restrict yourself to pure functions like I do ๐Ÿ˜€. called templeton that when given a scaffolding definition in a simple YAML file, will generate the corresponding project structure. I distilled out the project structure of magicsquare into this YAML file, so now whenever I create a new project, I just do:

templeton --template haskell.yaml --data Module=Foo --root foo

and get:

foo
โ”œโ”€โ”€ app
โ”‚  โ””โ”€โ”€ Main.hs
โ”œโ”€โ”€ examples
โ”‚  โ””โ”€โ”€ Examples.hs
โ”œโ”€โ”€ internal
โ”‚  โ””โ”€โ”€ FooInternal.hs
โ”œโ”€โ”€ src
โ”‚  โ””โ”€โ”€ Foo.hs
โ”œโ”€โ”€ test
โ”‚  โ””โ”€โ”€ Main.hs
โ”œโ”€โ”€ CHANGELOG.md
โ”œโ”€โ”€ foo.cabal
โ”œโ”€โ”€ LICENSE
โ””โ”€โ”€ README.md

In other news: I switched to Visual Studio Code for editing Haskell. Haskell LSP mode in Emacs was just too fidgety. I use Visual Studio Code at work, so the distance to adopting it for a Haskell setup was small. So much for all that Emacs talk/bragging I did in Haskell setup ๐Ÿ™‚. I use these two vscode extensions (not sure if both are needed):

It works well. Sometimes it gets stuck reporting a missing dependency even though cabal builds everything fine. Restarting vscode fixes it. ยฏ\__(ใƒ„)_/ยฏ