Per the assignment's instructions, the `Shift n` operation should treat
the *entire keyboard* like a cycle and shift that. I was erroneously
treating *each row* like a cycle and shifting those one-by-one.
This change fixes that. In addition, it also:
- Updates README.md with expected inputs and outputs
- Updates test suite
- Adds `split` dependency to {default,shell}.nix
TL;DR:
- include a default.nix to allow users to build an named executable
- emphasize in the README that the user needs Nix to build this project
- pin nixpkgs to a specific commit and fetch it from GitHub
Per my take-home assignment's reviewer's comments, with which for the record I
agree, I should generate the character->coordinate table from my existing qwerty
keyboard definition.
The best part is: by doing this I found a bug: Notice how the original '0'
character was mapped to the Coordinate (0,0)... thus every subsequent digit
key (i.e. the first row) is off-by-one.
After burning a few hours wrestling with the type system, I decided to revert to
the simpler `Server API` type instead of the `ServantT` transformer type.
The problem is that I couldn't write a MonadError instance for `RIO Context`,
which is my `AppM` (i.e. application monad). Using `throwIO` in the server
handlers results in 500 errors, which is not what I wanted. I'm still pretty
fuzzy about what's happening; I now know that exception handling in Haskell is
pretty gnaryly. I may revisit this at a later time when my knowledge is more
extensive. For now: time to fry bigger fish.
An easier abstract is for me to pass `T.Context` into `server` as an argument,
which after all is what a Reader does.
TL;DR:
- Read server, client ports from .envrc
- Define a top-level Failure type (empty for now)
- Define a top-level Success type
- Define App as RIO Context (Either Failure Success)
Long story -> short: I'd like to access my App monad from within my Servant
handlers.
While this code type-checks, I'm not sure it's working as intended. Needing to
change throwError to throwIO fails the "smell test". I expect to refactor this
code, but I'm calling it a night for now.
I believe RIO stands for: "ReaderT <something-something> IO", which is a nod to
the top-level application data type:
```haskell
-- This is a simplification
newtype RIO env a = RIO { runRIO :: ReaderT env a () }
```
I read about RIO from an FP-Complete blog post a few months ago, and now I'm
excited to try it out for a real project. Bon voyage!
As the previous commit mentions, I'm attempting to build and deploy this project
with `nix-shell` and `nix-build` instead of `cabal` and `stack`.
I'm in the Hamburg airport right now, and my internet connection isn't stable
enough to test this, so I'm committing it until I can more robustly test it.
I'd like to see if I can avoid using `cabal` and `stack` and build and deploy
this application using `nix-shell` and `nix-build` only. Let's see how that
goes.
The JWT should match "accounts.google.com" or "https://accounts.google.com". If
it doesn't, we produce a validation error.
TL;DR:
- Group all failed stringOrURI function calls as StringOrURIParseFailure errors
I'm attempting to be an obedient boy and implement this and future features
using TDD.
TL;DR:
- Defined a few tests
- Defined an empty GoogleSignIn module
- Defined a Fixtures module to quickly create JWTs to test
TL;DR:
- Add common dependencies like Servant, Aeson, Warp, Cors
- Define a POST /verify endpoint for our client to hit
- POST to /verify client-side onSignIn
Using Haskell's Text.ParserCombinators.ReadP library for the first time, and I
enjoyed it thoroughly! It's nice avoiding a third-party library like MegaParsec.
Before starting my take-home assignment, the instructions advised me to create a
"Hello, world" program in the language of my choice. Since I'm choosing Haskell,
I created this example as my starter boilerplate.
Many Bollywood movies have excellent acting, excellent directing, excellent
storytelling, but in my opinion, they spoil this with unnecessary musicals
interspersed throughout the films.
Dangal is a notable exception here. Overall, I'd say that this movie is
appropriately rated!
What a confusing movie this was... I may need to watch it a second time to
better understand what happened, but I found watching it once already so
exhausting that I'm not sure I'll ever watch it again.
Taken from the overview:
> I'm making this as an offline reference for some of the commands that I use
> often enough to need to remember but not often enough to *actually* remember.
The astute observer may notice that the number of entries in db.sqlite3 is fewer
than the number of unwatched movies in imdb-top-250.org. I'm at a lake in
Germany with Mimi and Cullen, so we took the intersection of my unwatched
movies, Mimi's unwatched movies, and Cullen's unwatched movies.
I needed to add the first step since I dipped into my Emergency fund last month
to pay for someone to prepare my US tax return. I added the other step as a
reminder.
Back when I owned an iPhone -- before I switched (mistakenly and thus
temporarily) to Android -- I had a note for each year's newly learned words. As
I am condensing as many of my documents as possible into my briefcase, I decided
it is time for including a dictionary.
I can still record words on my phone, and then I can transfer them to this
document.
In the beginning there existed only a generic //org directory... This directory
was generic enough to include any .org file regardless of its purpose, but
specific enough to disallow membership of other worthy files of the Markdown
ilk.
Then came the //playbooks directory, which robbed //org of most of its
inhabitants...
In the interim various .md and .org TODO lists existed scattered across the
landscape of the monorepo... some existed in far-away, exotic lands like
"travel-histlist"... These fractious tribes shared much in common with their
distant relatives, but the superficial differences granted the simple-minded,
draconian filesystem license to prevent them from mingling.
Then one day the monorepo had a new visitor: //todo-lists.
//todo-lists restored order to the monorepo, uniting all of the fractious
documents under one roof.
.md and .org files held hands and sang Kumbaya around a blazing fire for the
first time in history. All was well, and all were happy.
I'm particularly excited about this idea. As I was reading Graham's "Erase your
darlings" blog post, I had an idea: I should have playbooks at the root of my
monorepo.
I can have playbooks for the following:
- How to install NixOS
- How to build GCR images from Nix expressions
- A collection of miscellaneous shell commands (e.g. "how to kill a process by name")
- What series of steps should I follow when I receive a paycheck
I already keep README's at the root of each package, which I think is where many
of these instructions belong. Other tutorials that I write for myself that do
not belong to any package can go in //playbooks. I also will host my personal
habits in //playbooks since habits are a bit like playbooks for life. Let's see
how this idea ages as the caffeine wears off...