I'm working on a project in my spare time to make doing my home finances easier. It's nothing serious, and I'm massively over-engineering it so I can have fun & learn things.
Presently, I'm rewriting one of the components in Haskell. Hmm. That's not true. It would be fairer to say that I've realized that a chunk of the code presently in Python is actually a separate component that ought to be run as a separate service, and given that, I'm rewriting it in Haskell.
It's a simple service that connects to a SQLite database (not my database, not my schema) and exposes a narrow set of APIs for things I want to do from that interface.
So far, it's in very early stages, but I've already been faced with some interesting questions.
Writing the code in Haskell has forced me to think very early on about how and when I want to run database queries.
Doing a database query requires doing IO, and if a function does IO that changes the type of the function. For example, a function that gets the balance for an account by doing a query might have a type signature like this:
getBalance :: Connection -> Account -> IO Amount
However, if you've already got all the information loaded (perhaps you've loaded the entire database into memory), then the function might look this:
getBalance :: AllTheData -> Account -> Amount
If this were in Python, I could reasonably defer the decision, because there'd be some messy object representing the state which would either have a connection or all the data or both, and the getBalance
method would return an Amount
and the calling code wouldn't have to care whether it did IO or not.
Is this a good thing? I don't know. It's interesting though.