Loading and saving

Topics: parallel operations, Raise + exceptions + concurrency

Poké-Fun as provided has a very big limitation. You can only work on your deck in one go: if you want to devote several sessions to it, you must either (1) not close the application, or (2) write down your cards in a piece of paper and add them back the next time. In this section we add support for loading and saving deck files, and learn about high-level parallelism on the way.

Load and store

The first task is to implement saving the deck as a file, and being able to read it back afterward. Feel free to choose whatever format you like, from the list of identifiers separated by new lines, to some sort of JSON.

The code provided in deck/view.kt integrates FileKit to show the file picker of the platform the application is running on. The button are disabled, remember to set enabled = true in the call to IconButton.

Saving the deck is easy, but loading it potentially involves getting the information from each of them. You should use the getById method from PokemonTcgApi to retrieve the Card corresponding to a given identifier.

In a first approximation, using map over the sequence of identifiers should be enough. Albeit simple, that solution lacks performance. Arrow provides high-level concurrency which solves the problem quite succintly. Use parMap to turn the sequential iteration into a concurrent set of operations.

Another problem with the simple approach, depending on how you store the data from a deck, is that you may ask information about the same card more than once. One potential solution is to group the cards by identifier, but a more general approach is to use caching that works independently of the number of consumers.

From exceptions to Raise

Problems may arise during the retrieval of card information, but the current code is not prepared for that eventuality. In this section we improve the situation by using Raise.

About Raise

It is strongly recommended to read the Law-abiding decks section, which introduces the basics of Raise, before attempting the following task.

The integration of parMap (and parZip) with Raise and error accumulation is discussed in the Arrow documentation. Although the TL;DR is simply "replace mapOrAccumulate with parMapOrAccumulate and enjoy".

Your task is to use Either.catch to capture any potential exceptions, and transform then into Either. As hinted in the Law-abiding decks section, you need to define an error hierarchy to represent those problems.

Several error hierarchies

It is not necessary to have a single error hierarchy for the entire domain. You only need a common parent whenever you may be mixing those in a single function, which means your error hierarchy is actually shared by two parts of the domain.

By default, using the either builder means following a fail-first approach to errors. If you have not done it directly on the previous task, change the behavior to accumulation. In other words, you should report every problem you find loading a file, not only the first identifier you fail to obtain.