<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Roman Parykin</title>
    <link>https://donderom.com/</link>
    <description>Recent content on Roman Parykin</description>
    <language>en-us</language>
    <lastBuildDate>Wed, 09 Jul 2025 20:00:02 +0200</lastBuildDate>
    <atom:link href="https://donderom.com/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Managing nested models with Bubble Tea</title>
      <link>https://donderom.com/posts/managing-nested-models-with-bubble-tea/</link>
      <pubDate>Wed, 09 Jul 2025 20:00:02 +0200</pubDate>
      <guid>https://donderom.com/posts/managing-nested-models-with-bubble-tea/</guid>
      <description>&lt;div class=&#34;epigraph&#34;&gt;&#xA;  &lt;blockquote&gt;&#xA;    &#xA;    &lt;p&gt;&#xA;I don&#39;t get Bubbletea&#xA;&lt;/p&gt;&#xA;    &#xA;    &lt;footer&gt;&#xA;      r/golang, Reddit&#xA;      &#xA;      &#xA;    &lt;/footer&gt;&#xA;    &#xA;  &lt;/blockquote&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&lt;p&gt;Judging by other comments and similar posts, this experience is not unique.&lt;/p&gt;&#xA;&lt;p&gt;In this post, I’d like to share an intuition behind model composition using the &lt;a href=&#34;https://github.com/charmbracelet/bubbletea&#34;&gt;Bubble Tea&lt;/a&gt; TUI framework for Go, with the main focus on model nesting. While I assume a basic understanding of the Bubble Tea framework, let’s start by going over what a model is.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-elm-architecture&#34;&gt;The Elm Architecture&lt;/h2&gt;&#xA;&lt;p&gt;The Bubble Tea is based on &lt;a href=&#34;https://guide.elm-lang.org/architecture&#34;&gt;The Elm Architecture&lt;/a&gt;&lt;em&gt;&lt;small&gt; (The acronym TEA, short for The Elm Architecture, is what gives Bubble Tea its name.)&lt;/small&gt;&lt;/em&gt;, where the model is the central piece of application state, representing the entire state of the application at any given point in time. The other two components are:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;The &lt;code&gt;Update&lt;/code&gt; function that modifies the &lt;code&gt;model&lt;/code&gt; in response to messages.&lt;/li&gt;&#xA;&lt;li&gt;The &lt;code&gt;View&lt;/code&gt; function that uses the &lt;code&gt;model&lt;/code&gt; to determine what to show to the user.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;In Elm, the &lt;code&gt;model&lt;/code&gt; itself isn’t a formal concept, but a common pattern used to structure different states of the application. In practice, it’s a data structure that holds everything needed to render itself.&lt;/p&gt;&#xA;&lt;h2 id=&#34;nested-models&#34;&gt;Nested models&lt;/h2&gt;&#xA;&lt;p&gt;And that’s where the official tutorial ends, without even mentioning how to handle nested states, or, in other words, nested models. Let’s take a look at an  example called &lt;a href=&#34;https://github.com/charmbracelet/bubbletea/tree/main/examples/composable-views&#34;&gt;Composable Views&lt;/a&gt; from Bubble Tea’s &lt;a href=&#34;https://github.com/charmbracelet/bubbletea/tree/main/examples/#composable-views&#34;&gt;examples&lt;/a&gt; folder. The model, which composes two other models (timer and spinner), looks like this:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;type&lt;/span&gt; sessionState &lt;span style=&#34;&#34;&gt;uint&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;type&lt;/span&gt; mainModel &lt;span style=&#34;font-weight:bold&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;state   sessionState&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;timer   timer.Model&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;spinner spinner.Model&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;&#34;&gt;//...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The two models, &lt;code&gt;timer.Model&lt;/code&gt; and &lt;code&gt;spinner.Model&lt;/code&gt;&lt;em&gt;&lt;small&gt; (All &lt;code&gt;xyz.Model&lt;/code&gt;s are part of the Charm &lt;a href=&#34;https://github.com/charmbracelet/bubbles&#34;&gt;bubbles&lt;/a&gt; project.)&lt;/small&gt;&lt;/em&gt;, are nested within the &lt;code&gt;mainModel&lt;/code&gt;. The &lt;code&gt;sessionState&lt;/code&gt; is used to determine which model is currently active. Both the &lt;code&gt;Update&lt;/code&gt; and &lt;code&gt;View&lt;/code&gt; functions then branch their logic based on the current &lt;code&gt;sessionState&lt;/code&gt;, either routing messages to the appropriate model(s) or rendering the active model, respectively.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;  &lt;a href=&#34;https://donderom.com/posts/managing-nested-models-with-bubble-tea/&#34;&gt;View the diagrams on the website&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;As the number of nested models grows, we may want to move some of them into separate models, thereby creating a tree model structure.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;type&lt;/span&gt; subModel &lt;span style=&#34;font-weight:bold&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;time    timer.Model&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;spinner spinner.Model&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;type&lt;/span&gt; mainModel &lt;span style=&#34;font-weight:bold&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;state    sessionState&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;list     list.Model&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;subModel subModel&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&#xA;  &lt;a href=&#34;https://donderom.com/posts/managing-nested-models-with-bubble-tea/&#34;&gt;View the diagrams on the website&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;No matter the size of the model tree, the &lt;code&gt;mainModel&lt;/code&gt; remains the top-level model, responsible for routing messages to the child models and view rendering with the content of the child’s &lt;code&gt;View&lt;/code&gt; methods. For example, this is how updating the &lt;code&gt;timer.Model&lt;/code&gt; looks like:&lt;/p&gt;&#xA;&lt;p&gt;&#xA;  &lt;a href=&#34;https://donderom.com/posts/managing-nested-models-with-bubble-tea/&#34;&gt;View the diagrams on the website&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;There is nothing wrong with this approach. In fact, this is the recommended way to handle the nested models, as demonstrated in more detail in the &lt;a href=&#34;https://www.youtube.com/watch?v=uJ2egAkSkjg&#34;&gt;“Bubble Tea Nested Models”&lt;/a&gt; video. It makes perfect sense if the child models are part of the current view layout&lt;em&gt;&lt;small&gt; (Generalizing the modeling of components (or &lt;a href=&#34;https://github.com/charmbracelet/bubbles&#34;&gt;bubbles&lt;/a&gt;) with the current Go (1.25) type system is challenging. Although referred to as models, bubbles aren’t true models in the strict sense - they don’t implement the &lt;code&gt;tea.Model&lt;/code&gt; interface and they provide additional API. One possible workaround would be to make their API event-driven, but this would come at the cost of event loop performance.)&lt;/small&gt;&lt;/em&gt;. However, not all application models are like that. Some models represent entirely new screen views and have nothing to do with the main or previous model. Does this change the handling of nested models, as described above? Not at all. The main model is still responsible for tracking the current model and rendering the appropriate view in place of its own.&lt;/p&gt;&#xA;&lt;p&gt;To recap, the top-down approach is characterized by the following attributes:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Keeping track of the application state.&lt;/li&gt;&#xA;&lt;li&gt;Routing messages to the appropriate models in the &lt;code&gt;Update&lt;/code&gt; function based on the current state.&lt;/li&gt;&#xA;&lt;li&gt;Composing or delegating the view in the &lt;code&gt;View&lt;/code&gt; function based on the current state.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;independent-nested-models&#34;&gt;Independent nested models&lt;/h2&gt;&#xA;&lt;p&gt;Let’s bring in one more example to the picture, such as the &lt;a href=&#34;https://github.com/charmbracelet/bubbletea/tree/main/examples/credit-card-form&#34;&gt;Credit Card Form&lt;/a&gt;. The main model will consist of &lt;code&gt;list.Model&lt;/code&gt; and two submodels: &lt;code&gt;SubModel1&lt;/code&gt; for &lt;em&gt;Composable Views&lt;/em&gt; and &lt;code&gt;SubModel2&lt;/code&gt; for the &lt;em&gt;Credit Card Form&lt;/em&gt;. The main view might show a list of available examples (two, in this case). When the user selects an example, the corresponding view is shown instead of the list. Both &lt;code&gt;SubModel1&lt;/code&gt; and &lt;code&gt;SubModel2&lt;/code&gt; are completely independent of each other and of the main model. In this scenario, the &lt;code&gt;MainModel&lt;/code&gt;’s &lt;code&gt;Update&lt;/code&gt; and &lt;code&gt;View&lt;/code&gt; functions should handle at least three states. Even if the submodel’s view is self-contained, the &lt;code&gt;MainModel&lt;/code&gt;’s view should still be updated.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;  &lt;a href=&#34;https://donderom.com/posts/managing-nested-models-with-bubble-tea/&#34;&gt;View the diagrams on the website&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;em&gt;An independent model is one that contains all the information necessary to render a screen and handle user interactions.&lt;/em&gt; Examples of independent models include the main model and both submodels. In contrast, models like &lt;code&gt;list.Model&lt;/code&gt; or &lt;code&gt;textinput.Model&lt;/code&gt; are usually not independent, as they require additional models to manage user interaction. Some models can serve both roles simultaneously, such as both submodels. If a submodel is shown upon item selection, it is considered independent. However, if it’s shown alongside the list, it becomes part of the main model’s view and is therefore not independent.&lt;/p&gt;&#xA;&lt;p&gt;The steps for adding an independent model are closely tied to the top-down approach:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Add a new state to the main model.&lt;/li&gt;&#xA;&lt;li&gt;Extend the main model’s &lt;code&gt;Update&lt;/code&gt; function to handle the new state and route messages to the new model.&lt;/li&gt;&#xA;&lt;li&gt;Update the main model’s &lt;code&gt;View&lt;/code&gt; function to render the new model’s view based on the updated state.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;Let’s see how to get rid of all these steps with the &lt;strong&gt;Model Stack&lt;/strong&gt; architecture.&lt;/p&gt;&#xA;&lt;h2 id=&#34;model-stack-architecture&#34;&gt;Model Stack architecture&lt;/h2&gt;&#xA;&lt;p&gt;The concept behind the Model Stack architecture is to restructure a tree of nested models into a stack of independent ones. In the example above, the model tree can be decomposed into three independent models, stacked on top of one another as needed.&#xA;&lt;p&gt;&#xA;  &lt;a href=&#34;https://donderom.com/posts/managing-nested-models-with-bubble-tea/&#34;&gt;View the diagrams on the website&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Initially, the model stack contains only the entry model. Each model is responsible for managing its own state and knows how to render itself - it behaves just like any other component. How is another model displayed? It’s pushed onto the stack, becoming the current model. The model stack controller handles model initialization, routes messages to the current model, and displays the current model’s view.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;  &lt;a href=&#34;https://donderom.com/posts/managing-nested-models-with-bubble-tea/&#34;&gt;View the diagrams on the website&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;The model stack controller takes over routing for nested models - replacing the need for coordination required in the top-down approach within the main model’s &lt;code&gt;Update&lt;/code&gt; and &lt;code&gt;View&lt;/code&gt; functions - thus eliminating the steps for adding an independent model. The controller plays the role of the main model and intercepts all commands, but it does not need session state. All that’s left for models is to notify the controller.&lt;/p&gt;&#xA;&lt;h3 id=&#34;bubblon&#34;&gt;Bubblon&lt;/h3&gt;&#xA;&lt;p&gt;Let’s see the benefits of this approach using the &lt;a href=&#34;https://github.com/donderom/bubblon&#34;&gt;bubblon&lt;/a&gt; library, which implements the Model Stack architecture for the Bubble Tea framework.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Event-driven&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;Since the models are supposed to be independent, they cannot be accessed directly because the stack is encapsulated by the controller. The only way to interact with the controller is by sending commands. This allows any model to communicate with the controller without holding a direct reference to it.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Minimalistic&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;At its core, the Model Stack architecture is about simplification. For example, the &lt;code&gt;bubblon.Open&lt;/code&gt; command will initialize the provided model, make it the current one, and redirect messages to it. All of this is accomplished in a single line of code&lt;em&gt;&lt;small&gt; (Internally, there is a stack of models, but semantic API - using commands like &lt;code&gt;Open&lt;/code&gt; and &lt;code&gt;Close&lt;/code&gt; - makes more sense in the context of TUI apps.)&lt;/small&gt;&lt;/em&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;func&lt;/span&gt; (m MainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;font-style:italic&#34;&gt;// ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;font-weight:bold&#34;&gt;return&lt;/span&gt; m, bubblon.Open(SubModel1.New())&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When we’re done with the submodel and want to return to the main model:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;func&lt;/span&gt; (m SubModel1) Update(msg tea.Msg) (tea.Model, tea.Cmd) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;font-style:italic&#34;&gt;// ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;font-weight:bold&#34;&gt;return&lt;/span&gt; m, bubblon.Close&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Following its event-driven design, &lt;code&gt;bubblon&lt;/code&gt; will send the &lt;code&gt;MainModel&lt;/code&gt; the &lt;code&gt;bubblon.Closed&lt;/code&gt; message, indicating that the submodel has been closed.&lt;/p&gt;&#xA;&lt;p&gt;The API remains unobtrusive by introducing no new interfaces or concepts. Models are expected to implement the existing &lt;code&gt;tea.Model&lt;/code&gt; interface, and all interactions are performed using Bubble Tea commands. The controller itself is a &lt;code&gt;tea.Model&lt;/code&gt; and can be used like any other model.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Modular&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;One advantage of using independent models is &lt;em&gt;dynamic composition&lt;/em&gt;. Any model can become the current one. Model &lt;code&gt;A&lt;/code&gt; can open model &lt;code&gt;B&lt;/code&gt;, which in turn can open model &lt;code&gt;C&lt;/code&gt; - without model &lt;code&gt;A&lt;/code&gt; knowing anything about it. Or &lt;code&gt;C&lt;/code&gt; → &lt;code&gt;B&lt;/code&gt; → &lt;code&gt;A&lt;/code&gt;. No state, no cry. While Go prevents circular dependencies at the package level, it’s still possible to pass a constructed instance or a function to initialize a parent model.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Resource-efficient&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;By default, models are released and made available to the garbage collector once closed by the controller, rather than being kept indefinitely in the main model’s internal state. In addition, more efficient commands are available for replacing the top model - or all models - with a new one, without relying on a &lt;code&gt;Close/Open&lt;/code&gt; sequence: &lt;code&gt;bubblon.Replace&lt;/code&gt; and &lt;code&gt;bubblon.ReplaceAll&lt;/code&gt;&lt;em&gt;&lt;small&gt; (The full API reference can be found on &lt;a href=&#34;https://pkg.go.dev/github.com/donderom/bubblon&#34;&gt;go.dev&lt;/a&gt;.)&lt;/small&gt;&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Since the controller is not responsible for model creation, it is not limited to dynamic composition. Otherwise it would be a waste of resources to recreate a model that can be instantiated just once. This is where a &lt;em&gt;hybrid approach&lt;/em&gt; to model caching comes in. A submodel that is better suited for one-time creation can be embedded for repeated use, with all the other benefits of the Model Stack architecture preserved.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;type&lt;/span&gt; model &lt;span style=&#34;font-weight:bold&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;submodel tea.Model&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;// somewhere&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;// m := model{&amp;amp;submodel{}}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;func&lt;/span&gt; (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;font-style:italic&#34;&gt;// Maybe reset submodel&amp;#39;s state first&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;font-weight:bold&#34;&gt;return&lt;/span&gt; m, bubblon.Open(m.submodel)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Without any session state or command coordination, the submodel is cached by the main model and can be reused at any time. The goal of the Model Stack architecture is to enable both dynamic composition and model caching with minimal code changes.&lt;/p&gt;&#xA;&lt;p&gt;To summarize, there are at least three progressive levels for building Bubble Tea applications using nested models with &lt;a href=&#34;https://github.com/donderom/bubblon&#34;&gt;bubblon&lt;/a&gt;:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Level 1&lt;/strong&gt;: Use top-down approach to build a model from individual components.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Level 2&lt;/strong&gt;: Use the Model Stack architecture to compose an application from independent models.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Level 3&lt;/strong&gt;: Use a hybrid approach with the Model Stack architecture to maximize flexibility and performance.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Check out &lt;a href=&#34;https://github.com/donderom/bubblon&#34;&gt;bubblon&lt;/a&gt; on GitHub for more practical examples.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Stretchy architecture</title>
      <link>https://donderom.com/posts/stretchy-architecture/</link>
      <pubDate>Sun, 23 Jul 2023 16:33:52 +0300</pubDate>
      <guid>https://donderom.com/posts/stretchy-architecture/</guid>
      <description>&lt;p&gt;&lt;span class=&#34;newthought&#34;&gt;Stretchy architecture&lt;/span&gt;&#xA; is an architectural approach where each dimension is chosen in a way that makes it easier to move in one direction on the decision spectrum than the other. An important condition is to move back to the starting point if possible.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Asynchronous vs synchronous communication&lt;/strong&gt;. Usually presented as a trade-off, in the stretchy architecture style, moving from async to sync is fine and, in practice, it&amp;rsquo;s much easier then the other way round. Starting with async communication by default allows switching to sync and back anytime if needed. Switching from sync to async is not only more difficult but might also be impossible, which locks (&lt;em&gt;see?&lt;/em&gt;) you into the sync style.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Eventual vs strong consistency&lt;/strong&gt;. Stick to eventual consistency (or any other weaker consistency model), and any part can be made atomic without changing much of the rest. Suggesting to replace the existing strong consistency with weaker models might be dangerous for the messenger. This friction is what makes some people argue about which style is better. Neither is; it&amp;rsquo;s just easier to start with a weaker model and move towards stronger ones if new requirements dictate that.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Choreography vs orchestrated coordination&lt;/strong&gt;. Besides the tight coupling introduced by the mediator, orchestration implementations often lead to synchronous communication, where components must wait for instructions from the orchestrator. On the other hand, a fully choreographed system offers the flexibility to add an orchestrator to facilitate coordination among components if the need arises.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Immutability vs mutability&lt;/strong&gt;. Given a piece of immutable code, it&amp;rsquo;s fine to introduce mutability in certain parts for performance reasons, as long as it remains isolated from the rest of the codebase. It&amp;rsquo;s a perfect illustration of &amp;ldquo;stretchiness&amp;rdquo;, where we begin with immutable design, stretch it a bit to benefit from lower-level optimizations, and then return back to immutability.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Stateless vs stateful protocol&lt;/strong&gt;. The recurring pattern of increased friction of movement in one direction more than the other is a defining characteristic of the stretchy architecture. Can be confirmed by anyone who has tried to transition from a stateful protocol to a stateless one.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Schema vs schemaless modeling&lt;/strong&gt;. Having a schema for data modeling condones hacks like treating JSON objects as text or bytes. When everything is text, where do you go from there?&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Static vs dynamic typing&lt;/strong&gt;. Similar to data modeling but applied to code, statically typed languages provide more guarantees out of the box, and most of them are capable of loosening data representation. One of the useful patterns is to parse data as soon as possible (for example, when reading a file) and work with typed data afterwards. With a map of &lt;code&gt;any&lt;/code&gt; values by &lt;code&gt;any&lt;/code&gt; keys, the code is destined to become increasingly problematic.&lt;/p&gt;&#xA;&lt;p&gt;Thinking in terms of the stretchy architecture results in responsive, highly-available, scalable, fault-tolerant, decoupled, and maintainable systems - essentially for free.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Hugging Face pipeline inference optimization</title>
      <link>https://donderom.com/posts/transformers-pipeline-inference-optimization/</link>
      <pubDate>Sun, 19 Feb 2023 14:43:36 +0200</pubDate>
      <guid>https://donderom.com/posts/transformers-pipeline-inference-optimization/</guid>
      <description>&lt;p&gt;The goal of this post is to show how to apply a few practical optimizations to improve inference performance of &lt;a href=&#34;https://huggingface.co/docs/transformers/index&#34;&gt;🤗 Transformers&lt;/a&gt; pipelines on a single GPU. Compatibility with pipeline API is the driving factor behind the selection of approaches for inference optimization. This is a practical guide to optimizing inference of 🤗 Transformers pipelines based on my personal experience. For more methods on how to make transformer inference more efficient, I recommend checking out Lilian Weng&amp;rsquo;s blog&lt;em&gt;&lt;small&gt; (&lt;a href=&#34;https://lilianweng.github.io/posts/2023-01-10-inference-optimization/&#34;&gt;Weng, Lilian. (Jan 2023). Large Transformer Model Inference Optimization. Lil’Log&lt;/a&gt;.)&lt;/small&gt;&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;All the code is run on NVIDIA RTX 4090 24G using Python 3.10 and PyTorch 1.13.1.&#xA;Other dependencies are:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;datasets==2.9.0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;evaluate[evaluator]==0.4.0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;optimum[onnxruntime-gpu]==1.6.4&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;transformers==4.26.1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To start off, let&amp;rsquo;s establish the baseline and evaluation method, which will remain consistent across all approaches.&lt;/p&gt;&#xA;&lt;h2 id=&#34;baseline&#34;&gt;Baseline&lt;/h2&gt;&#xA;&lt;p&gt;As a baseline, I&amp;rsquo;m going to use the &lt;a href=&#34;https://huggingface.co/roberta-base&#34;&gt;RoBERTa base&lt;/a&gt; model fine-tuned on the &lt;a href=&#34;https://huggingface.co/datasets/rajpurkar/squad&#34;&gt;SQuAD&lt;/a&gt; dataset for extractive question answering. The training is done using the scripts from the &lt;a href=&#34;https://github.com/huggingface/transformers&#34;&gt;transformers&lt;/a&gt; &lt;a href=&#34;https://github.com/huggingface/transformers/tree/v4.26.1/examples/pytorch/question-answering&#34;&gt;examples&lt;/a&gt;&lt;em&gt;&lt;small&gt; (All training examples are based on the &lt;code&gt;v4.26.1&lt;/code&gt; tag.)&lt;/small&gt;&lt;/em&gt; for PyTorch.&lt;/p&gt;&#xA;&lt;p&gt;First, let&amp;rsquo;s fine-tune the &lt;code&gt;roberta-base&lt;/code&gt; model on the &lt;code&gt;squad&lt;/code&gt; dataset for two epochs with the following parameters (to match the examples from the &lt;code&gt;transformers&lt;/code&gt;):&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python run_qa.py &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;&lt;/span&gt;  --model_name_or_path roberta-base &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;&lt;/span&gt;  --dataset_name rajpurkar/squad &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;&lt;/span&gt;  --do_train &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;&lt;/span&gt;  --per_device_train_batch_size 32 &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;&lt;/span&gt;  --learning_rate 3e-5 &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;&lt;/span&gt;  --num_train_epochs 2 &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;&lt;/span&gt;  --save_strategy no &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;&lt;/span&gt;  --output_dir /workspace/roberta-base-squad&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;pipeline-evaluation&#34;&gt;Pipeline Evaluation&lt;/h3&gt;&#xA;&lt;p&gt;The following code is going to be used for the 🤗 Transformers pipeline&lt;em&gt;&lt;small&gt; (The official tutorial on how to use pipelines for inference can be found &lt;a href=&#34;https://huggingface.co/docs/transformers/pipeline_tutorial&#34;&gt;here&lt;/a&gt;.)&lt;/small&gt;&lt;/em&gt; evaluation:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-py&#34; data-lang=&#34;py&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;datasets&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; load_dataset&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;evaluate&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; evaluator&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;transformers&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; pipeline&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Metrics = dict[str, float]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;batch_size = 8&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;data = load_dataset(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;rajpurkar/squad&amp;#39;&lt;/span&gt;, split=&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;validation&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;qa_evaluator = evaluator(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;question-answering&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;def&lt;/span&gt; qa_eval(model, tokenizer) -&amp;gt; Metrics:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    pipe = pipeline(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        task=&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;question-answering&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        model=model,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        tokenizer=tokenizer,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        batch_size=batch_size,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        device=0,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    metrics = qa_evaluator.compute(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        model_or_pipeline=pipe,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        data=data,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        metric=&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;squad&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        device=0,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-weight:bold&#34;&gt;return&lt;/span&gt; {k: round(v, 4) &lt;span style=&#34;font-weight:bold&#34;&gt;for&lt;/span&gt; k, v &lt;span style=&#34;font-weight:bold&#34;&gt;in&lt;/span&gt; metrics.items()}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With the fine-tuned model and evaluation method in place, we can establish the baseline as follows:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-py&#34; data-lang=&#34;py&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;transformers&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; AutoModelForQuestionAnswering, AutoTokenizer&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;model_name = &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;/workspace/roberta-base-squad&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;model = AutoModelForQuestionAnswering.from_pretrained(model_name).to(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;cuda&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;tokenizer = AutoTokenizer.from_pretrained(model_name)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;qa_eval(model=model, tokenizer=tokenizer)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;exact_match&amp;#39;&lt;/span&gt;: 85.6481, &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;f1&amp;#39;&lt;/span&gt;: 92.1859, &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;samples_per_second&amp;#39;&lt;/span&gt;: 293.3532}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The accuracy metrics are &lt;code&gt;exact_match&lt;/code&gt; and &lt;code&gt;f1&lt;/code&gt;, while the throughput is reported as &lt;code&gt;samples_per_second&lt;/code&gt;. The important thing to note is that the numbers are for the pipeline and not the model itself, as the pipeline has extra logic for computing the best answer. Additionally, there is overhead caused by the evaluation. According to which, the pipeline baseline is indicated by an &lt;code&gt;f1&lt;/code&gt; score of 92.1859 and a throughput of 293 samples per second. The exact numbers are not that significant as relative performance improvements compared to the baseline.&lt;/p&gt;&#xA;&lt;p&gt;With the baseline and evaluation out of the way, let&amp;rsquo;s see how to apply the first optimization technique&amp;mdash;knowledge distillation.&lt;/p&gt;&#xA;&lt;h2 id=&#34;knowledge-distillation&#34;&gt;Knowledge Distillation&lt;/h2&gt;&#xA;&lt;p&gt;&lt;em&gt;Knowledge distillation&lt;/em&gt; is a training technique for transferring knowledge from a pre-trained model (&amp;ldquo;teacher&amp;rdquo;) to a less complex one (&amp;ldquo;student&amp;rdquo;).&lt;/p&gt;&#xA;&lt;p&gt;In some cases, task-specific knowledge distillation refers to the process of fine-tuning already distilled language model such as DistilBERT. Let&amp;rsquo;s see what we&amp;rsquo;ll get by fine-tuning the &lt;a href=&#34;https://huggingface.co/distilroberta-base&#34;&gt;DistilRoBERTa base&lt;/a&gt; model on the SQuAD with the same parameters:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python run_qa.py &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;&lt;/span&gt;  --model_name_or_path distilroberta-base &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;&lt;/span&gt;  --dataset_name rajpurkar/squad &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;&lt;/span&gt;  --do_train &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;&lt;/span&gt;  --per_device_train_batch_size 32 &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;&lt;/span&gt;  --learning_rate 3e-5 &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;&lt;/span&gt;  --num_train_epochs 2 &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;&lt;/span&gt;  --save_strategy no &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;&lt;/span&gt;  --output_dir /workspace/distilroberta-base-squad&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Evaluation:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-py&#34; data-lang=&#34;py&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;transformers&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; AutoModelForQuestionAnswering, AutoTokenizer&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;model_name = &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;/workspace/distilroberta-base-squad&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;model = AutoModelForQuestionAnswering.from_pretrained(model_name).to(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;cuda&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;tokenizer = AutoTokenizer.from_pretrained(model_name)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;qa_eval(model=model, tokenizer=tokenizer)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;exact_match&amp;#39;&lt;/span&gt;: 80.5109, &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;f1&amp;#39;&lt;/span&gt;: 87.7095, &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;samples_per_second&amp;#39;&lt;/span&gt;: 381.8372}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Although a 5% drop in pipeline accuracy might be the biggest trade-off to make, a 30% increase in speed is still quite appealing, especially when combined with other techniques. Let&amp;rsquo;s see if we can do better with two-step knowledge distillation.&lt;/p&gt;&#xA;&lt;h3 id=&#34;two-step-distillation&#34;&gt;Two-step distillation&lt;/h3&gt;&#xA;&lt;blockquote cite=&#34;https://arxiv.org/abs/1910.01108&#34;&gt;&#xA;  &lt;p&gt;We also studied whether we could add another step of distillation during the adaptation phase by fine-tuning DistilBERT on SQuAD using a BERT model previously fine-tuned on SQuAD as a teacher for an additional term in the loss (knowledge distillation). In this setting, there are thus two successive steps of distillation, one during the pre-training phase and one during the adaptation phase. In this case, we were able to reach interesting performances given the size of the model: 79.8 F1 and 70.4 EM, i.e. within 3 points of the full model.&lt;/p&gt;&#xA;  &lt;footer&gt;&lt;a href=https://arxiv.org/abs/1910.01108&gt;Sanh, V.; Debut, L.; Chaumond, J. &amp;amp; Wolf, T. (2019), &amp;#39;DistilBERT, a distilled version of BERT: smaller, faster, cheaper and lighter&amp;#39;&lt;/a&gt;&lt;/footer&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;p&gt;Let me show how to implement two-step knowledge distillation to improve accuracy while keeping the throughput. In order to do this, we need to add one file to the same folder where the &lt;code&gt;run_qa.py&lt;/code&gt; script is (&lt;code&gt;examples/pytorch/question-answering&lt;/code&gt;):&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;distil_trainer_qa.py&lt;/code&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-py&#34; data-lang=&#34;py&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;dataclasses&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; dataclass, field&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;typing&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; Optional&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;torch&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;torch.nn.functional&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;as&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;F&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;transformers&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; AutoModelForQuestionAnswering, TrainingArguments&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;trainer_qa&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; QuestionAnsweringTrainer&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@dataclass&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;DistilTrainingArguments&lt;/span&gt;(TrainingArguments):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    alpha: float = field(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        default=0.25,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        metadata={&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;help&amp;#34;&lt;/span&gt;: &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;Controls the relative strength of each loss&amp;#34;&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    temperature: int = field(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        default=3,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        metadata={&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;help&amp;#34;&lt;/span&gt;: &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;Scaling factor to soften probabilities&amp;#34;&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    teacher_name_or_path: Optional[str] = field(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        default=&lt;span style=&#34;font-weight:bold&#34;&gt;None&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        metadata={&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;help&amp;#34;&lt;/span&gt;: &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;Teacher model name or path&amp;#34;&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;DistilQuestionAnsweringTrainer&lt;/span&gt;(QuestionAnsweringTrainer):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-weight:bold&#34;&gt;def&lt;/span&gt; __init__(self, *args, **kwargs):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        super().__init__(*args, **kwargs)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self.teacher = &lt;span style=&#34;font-weight:bold&#34;&gt;None&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-weight:bold&#34;&gt;if&lt;/span&gt; self.args.teacher_name_or_path:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            self.teacher = AutoModelForQuestionAnswering.from_pretrained(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                self.args.teacher_name_or_path&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            self.teacher.to(self.args.device).eval()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self.loss_fn = torch.nn.KLDivLoss(reduction=&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;batchmean&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self.alpha = self.args.alpha&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self.T = self.args.temperature&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self.T2 = self.T ** 2&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-weight:bold&#34;&gt;def&lt;/span&gt; compute_loss(self, model, inputs, return_outputs=&lt;span style=&#34;font-weight:bold&#34;&gt;False&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-weight:bold&#34;&gt;if&lt;/span&gt; self.teacher:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            outputs_stu = model(**inputs)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;font-weight:bold&#34;&gt;with&lt;/span&gt; torch.no_grad():&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                outputs_tea = self.teacher(**inputs)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            loss_start = self.T2 * self.loss_fn(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                F.log_softmax(outputs_stu.start_logits / self.T, dim=-1),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                F.softmax(outputs_tea.start_logits / self.T, dim=-1)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            loss_end = self.T2 * self.loss_fn(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                F.log_softmax(outputs_stu.end_logits / self.T, dim=-1),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                F.softmax(outputs_tea.end_logits / self.T, dim=-1)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            loss_kd = (loss_start + loss_end) / 2.0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;font-style:italic&#34;&gt;# Overall loss as a weighted sum&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            loss = self.alpha * outputs_stu.loss + (1.0 - self.alpha) * loss_kd&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;font-weight:bold&#34;&gt;return&lt;/span&gt; (loss, outputs_stu) &lt;span style=&#34;font-weight:bold&#34;&gt;if&lt;/span&gt; return_outputs &lt;span style=&#34;font-weight:bold&#34;&gt;else&lt;/span&gt; loss&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-weight:bold&#34;&gt;else&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;font-weight:bold&#34;&gt;return&lt;/span&gt; super().compute_loss(model, inputs, return_outputs)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It extends the &lt;code&gt;TrainingArguments&lt;/code&gt; by adding three new parameters:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;alpha&lt;/code&gt;: Controls how much weight is put on the student-teacher loss relative to the student loss alone, with higher value giving more weight to the student loss.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;temperature&lt;/code&gt;: Controls the level of smoothing of the teacher probability distribution, with higher temperature leading to a softer distribution.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;teacher_name_or_path&lt;/code&gt;: Name or path to the pre-trained teacher model.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;The default values for &lt;code&gt;alpha&lt;/code&gt; and &lt;code&gt;temperature&lt;/code&gt; are set as a result of a dozen of &lt;a href=&#34;https://github.com/optuna/optuna&#34;&gt;Optuna&lt;/a&gt; trials&lt;em&gt;&lt;small&gt; (Hyperparameter space: 𝛼 ∈ [0, 1], T ∈ [1, 10].)&lt;/small&gt;&lt;/em&gt; for this specific task. In general, for many NLP tasks a good starting point would be &lt;code&gt;alpha &amp;lt; 0.3&lt;/code&gt; and &lt;code&gt;temperature &amp;gt; 1&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;The &lt;code&gt;DistilQuestionAnsweringTrainer&lt;/code&gt; class extends the &lt;code&gt;QuestionAnsweringTrainer&lt;/code&gt; from the &lt;code&gt;trainer_qa.py&lt;/code&gt; file by overriding the &lt;code&gt;compute_loss&lt;/code&gt; function. The new loss function involves logits from the teacher to calculate the overall loss as a weighted sum of the student&amp;rsquo;s loss and the distillation loss using the new &lt;code&gt;alpha&lt;/code&gt; and &lt;code&gt;temperature&lt;/code&gt; parameters.&lt;/p&gt;&#xA;&lt;p&gt;In addition to this new file, the &lt;code&gt;run_qa.py&lt;/code&gt; itself should be modified to support the new distillation trainer:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;run_qa.py&lt;/code&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-diff&#34; data-lang=&#34;diff&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;from utils_qa import postprocess_qa_predictions&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+ from distil_trainer_qa import DistilTrainingArguments, DistilQuestionAnsweringTrainer&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- parser = HfArgumentParser((ModelArguments, DataTrainingArguments, TrainingArguments)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+ parser = HfArgumentParser((ModelArguments, DataTrainingArguments, DistilTrainingArguments))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- trainer = QuestionAnsweringTrainer(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+ trainer = DistilQuestionAnsweringTrainer(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If the changes have been applied correctly, training with two-step knowledge distillation should be as easy as adding the &lt;code&gt;--teacher&lt;/code&gt; parameter (when leaving the &lt;code&gt;alpha&lt;/code&gt; and the &lt;code&gt;temperature&lt;/code&gt; parameters as default):&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python run_qa.py &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;&lt;/span&gt;  --model_name_or_path distilroberta-base &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;&lt;/span&gt;  --dataset_name rajpurkar/squad &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;&lt;/span&gt;  --do_train &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;&lt;/span&gt;  --per_device_train_batch_size 32 &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;&lt;/span&gt;  --learning_rate 3e-5 &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;&lt;/span&gt;  --num_train_epochs 2 &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;&lt;/span&gt;  --save_strategy no &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;&lt;/span&gt;  --output_dir /workspace/distilroberta-base-distilsquad &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;&lt;/span&gt;  --teacher /workspace/roberta-base-squad&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If there is any justice in the world, bringing teacher into the equation should move accuracy closer to our baseline.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-py&#34; data-lang=&#34;py&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;transformers&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; AutoModelForQuestionAnswering, AutoTokenizer&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;model_name = &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;/workspace/distilroberta-base-distilsquad&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;model = AutoModelForQuestionAnswering.from_pretrained(model_name).to(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;cuda&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;tokenizer = AutoTokenizer.from_pretrained(model_name)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;qa_eval(model=model, tokenizer=tokenizer)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;exact_match&amp;#39;&lt;/span&gt;: 81.6746, &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;f1&amp;#39;&lt;/span&gt;: 88.7431, &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;samples_per_second&amp;#39;&lt;/span&gt;: 384.2462}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The two-step distillation process maintains a throughput of 31% over the baseline, retaining 96% of the baseline accuracy. Increasing the number of epochs (and playing with other hyperparameters) will bump this number even further&lt;em&gt;&lt;small&gt; (The &lt;a href=&#34;https://arxiv.org/abs/1910.01108&#34;&gt;DistilBERT&lt;/a&gt; paper reports &amp;ldquo;within 3 points of the full model&amp;rdquo;.)&lt;/small&gt;&lt;/em&gt; without any negative effect on throughput. I will use this distilled model for the upcoming set of methods.&lt;/p&gt;&#xA;&lt;h2 id=&#34;automatic-mixed-precision&#34;&gt;Automatic Mixed Precision&lt;/h2&gt;&#xA;&lt;p&gt;&lt;em&gt;Automatic Mixed Precision (AMP)&lt;/em&gt; allows to use a mix of &lt;code&gt;torch.float32&lt;/code&gt; and half-precision (&lt;code&gt;torch.float16&lt;/code&gt;) floating point datatypes during inference, thereby reducing the memory footprint and improving performance while maintaining accuracy.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-py&#34; data-lang=&#34;py&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;torch&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;transformers&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; AutoModelForQuestionAnswering, AutoTokenizer&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;model_name = &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;/workspace/distilroberta-base-distilsquad&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;model = AutoModelForQuestionAnswering.from_pretrained(model_name).to(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;cuda&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;tokenizer = AutoTokenizer.from_pretrained(model_name)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;# autocast context manager allows code regions to run in mixed precision&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;with&lt;/span&gt; torch.cuda.amp.autocast(dtype=torch.float16):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(qa_eval(model=model, tokenizer=tokenizer))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;exact_match&amp;#39;&lt;/span&gt;: 81.7029, &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;f1&amp;#39;&lt;/span&gt;: 88.7576, &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;samples_per_second&amp;#39;&lt;/span&gt;: 416.5399}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In addition to the performance improvement by 8.4% compared to the original model&lt;em&gt;&lt;small&gt; (Double that for the non-distilled baseline model.)&lt;/small&gt;&lt;/em&gt;, there is also a nearly 0.03% increase in accuracy as well. In general, it&amp;rsquo;s a safe approach to use the AMP &lt;code&gt;autocast&lt;/code&gt; with language models. Later on, we&amp;rsquo;ll see that, even in situations where it does not impact the performance, it typically does not cause any harm.&lt;/p&gt;&#xA;&lt;h2 id=&#34;torchscript&#34;&gt;TorchScript&lt;/h2&gt;&#xA;&lt;p&gt;&lt;em&gt;TorchScript&lt;/em&gt; is a way to create serializable and optimizable models from PyTorch code&lt;em&gt;&lt;small&gt; (From PyTorch documentation on &lt;a href=&#34;https://pytorch.org/docs/stable/jit.html&#34;&gt;TorchScript&lt;/a&gt;.)&lt;/small&gt;&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;There are two ways to use TorchScript models with pipelines:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Tracing an existing model.&lt;/li&gt;&#xA;&lt;li&gt;Loading a TorchScript module to use for inference.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;We&amp;rsquo;ll now explore how to trace an existing model to use for pipeline inference, as loading a TorchScript module will be straightforward with tricks used for tracing.&lt;/p&gt;&#xA;&lt;p&gt;There are three steps to trace a model to use it with pipeline:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Prepare example inputs that will be passed to the model.&lt;/li&gt;&#xA;&lt;li&gt;Trace the model to capture its structure by evaluating it using example inputs.&lt;/li&gt;&#xA;&lt;li&gt;Patch existing model to use TorchScript for forward pass.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-py&#34; data-lang=&#34;py&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;datasets&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; load_dataset&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;torch&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;transformers&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; AutoModelForQuestionAnswering, AutoTokenizer&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;transformers.modeling_outputs&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; QuestionAnsweringModelOutput&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;model_name = &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;/workspace/distilroberta-base-distilsquad&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;model = AutoModelForQuestionAnswering.from_pretrained(model_name, torchscript=&lt;span style=&#34;font-weight:bold&#34;&gt;True&lt;/span&gt;).to(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;cuda&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;tokenizer = AutoTokenizer.from_pretrained(model_name)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;def&lt;/span&gt; encode(examples):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-weight:bold&#34;&gt;return&lt;/span&gt; tokenizer(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        examples[&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;question&amp;#39;&lt;/span&gt;],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        examples[&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;context&amp;#39;&lt;/span&gt;],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        truncation=&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;only_second&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        max_length=384,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        stride=128,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        padding=&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;max_length&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;# 1. Prepare example inputs&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;squad = load_dataset(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;rajpurkar/squad&amp;#39;&lt;/span&gt;, split=&lt;span style=&#34;font-style:italic&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;validation[:&lt;/span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;{&lt;/span&gt;batch_size&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;]&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;example_batch = squad.map(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    encode,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    batched=&lt;span style=&#34;font-weight:bold&#34;&gt;True&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    remove_columns=squad.column_names&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;).with_format(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;torch&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;# 2. Trace the model&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;jit_inputs = (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    example_batch[&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;input_ids&amp;#39;&lt;/span&gt;].to(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;cuda&amp;#39;&lt;/span&gt;),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    example_batch[&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;attention_mask&amp;#39;&lt;/span&gt;].to(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;cuda&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;jit_model = torch.jit.trace(model, jit_inputs)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;jit_model = torch.jit.freeze(jit_model).to(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;cuda&amp;#39;&lt;/span&gt;).eval()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;# 3. Patch the model forward function to be compatible with pipeline&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;def&lt;/span&gt; forward_qa_wrap(**inputs):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-weight:bold&#34;&gt;with&lt;/span&gt; torch.jit.optimized_execution(&lt;span style=&#34;font-weight:bold&#34;&gt;False&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        start_logits, end_logits = jit_model(**inputs)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-weight:bold&#34;&gt;return&lt;/span&gt; QuestionAnsweringModelOutput(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        start_logits=start_logits,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        end_logits=end_logits&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;model.forward = forward_qa_wrap&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;with&lt;/span&gt; torch.cuda.amp.autocast(dtype=torch.float16):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(qa_eval(model=model, tokenizer=tokenizer))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;exact_match&amp;#39;&lt;/span&gt;: 81.7029, &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;f1&amp;#39;&lt;/span&gt;: 88.7576, &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;samples_per_second&amp;#39;&lt;/span&gt;: 446.3015}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The performance increase is 16% compared to the original distilled model, which is almost twice the improvement achieved by &lt;code&gt;autocast&lt;/code&gt; alone.&lt;/p&gt;&#xA;&lt;p&gt;There are several important tricks to get there:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;TorchScript is optimized using just-in-time (JIT) compilation based on inputs. When inputs are different (and they are in the case of question answering), JIT recompilation will slow down the pipeline performance eventually. To keep the performance stable, it&amp;rsquo;s essential to run TorchScript inside the &lt;code&gt;torch.jit.optimized_execution(False)&lt;/code&gt; context manager.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;The pipeline for question answering expects a model to return an instance of the &lt;code&gt;QuestionAnsweringModelOutput&lt;/code&gt; class, while TorchScript will return a tuple of logits. To make it compatible with the pipeline API, it should be wrapped in a function mimicking the forward call. The original model&amp;rsquo;s forward function is then replaced with the new wrapper&lt;em&gt;&lt;small&gt; (This is a more general technique that could be used to wrap the NVIDIA &lt;a href=&#34;https://github.com/nvidia/apex&#34;&gt;Apex&lt;/a&gt; for example.)&lt;/small&gt;&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Using TorchScript along with &lt;code&gt;autocast&lt;/code&gt; is crucial for inference optimization.  In my experience, utilizing &lt;code&gt;autocast&lt;/code&gt; during inference instead of tracing itself results in a slight improvement in performance.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;While this approach may result in less noticeable performance improvements on some GPUs, such as the RTX 30 series, it is more stable from run to run than AMP alone.&lt;/p&gt;&#xA;&lt;h2 id=&#34;deepspeed&#34;&gt;DeepSpeed&lt;/h2&gt;&#xA;&lt;p&gt;Setting up &lt;a href=&#34;https://www.deepspeed.ai&#34;&gt;DeepSpeed&lt;/a&gt; integration requires more effort if there are missing system dependencies, such as Python shared libs and header files&lt;em&gt;&lt;small&gt; (More about DeepSpeed installation &lt;a href=&#34;https://www.deepspeed.ai/tutorials/advanced-install&#34;&gt;here&lt;/a&gt;.)&lt;/small&gt;&lt;/em&gt;. If you are using DeepSpeed for training, you can leverage &lt;a href=&#34;https://www.deepspeed.ai/inference&#34;&gt;DeepSpeed-Inference&lt;/a&gt; to perform inference as well. Despite the fact that DeepSpeed-Inference shares its name with DeepSpeed, it does not use &lt;a href=&#34;https://arxiv.org/abs/1910.02054&#34;&gt;ZeRO&lt;/a&gt; (Zero Redundancy Optimizer) technology.&lt;/p&gt;&#xA;&lt;p&gt;Using DeepSpeed-Inference with the pipeline API is straightforward&lt;em&gt;&lt;small&gt; (DeepSpeed-Inference &lt;a href=&#34;https://www.deepspeed.ai/tutorials/inference-tutorial&#34;&gt;tutorial&lt;/a&gt;.)&lt;/small&gt;&lt;/em&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-py&#34; data-lang=&#34;py&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;deepspeed&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;torch&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;transformers&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; AutoModelForQuestionAnswering, AutoTokenizer&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;model_name = &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;/workspace/distilroberta-base-distilsquad&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;model = AutoModelForQuestionAnswering.from_pretrained(model_name).to(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;cuda&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;tokenizer = AutoTokenizer.from_pretrained(model_name)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;engine = deepspeed.init_inference(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    model,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    dtype=torch.float16,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    replace_with_kernel_inject=&lt;span style=&#34;font-weight:bold&#34;&gt;True&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;with&lt;/span&gt; torch.cuda.amp.autocast(dtype=torch.float16):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(qa_eval(model=engine.module, tokenizer=tokenizer))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Although I don&amp;rsquo;t have the exact numbers for the same GPU used in the other methods, it consistently outperforms the previous approach (TorchScript with AMP) by 5&amp;ndash;7% on the RTX 3090 24G.&lt;/p&gt;&#xA;&lt;h2 id=&#34;optimum&#34;&gt;Optimum&lt;/h2&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://github.com/huggingface/optimum&#34;&gt;🤗 Optimum&lt;/a&gt; is an extension of 🤗 Transformers, providing a set of  performance optimization tools with unified API to train and run models on targeted hardware with maximum efficiency&lt;em&gt;&lt;small&gt; (More about 🤗 Optimum can be found in the &lt;a href=&#34;https://huggingface.co/docs/optimum/index&#34;&gt;official documentation&lt;/a&gt;.)&lt;/small&gt;&lt;/em&gt;. Optimum can be used for accelerated inference with built-in support for transformers pipelines, making it an ideal candidate for pipeline optimization techniques. I will demonstrate how to apply graph optimization to accelerate inference with ONNX Runtime.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://onnxruntime.ai&#34;&gt;ONNX Runtime&lt;/a&gt; (ORT) is a cross-platform, high-performance engine for &lt;a href=&#34;https://onnx.ai&#34;&gt;Open Neural Network Exchange (ONNX)&lt;/a&gt; models used to accelerate inference and training of machine learning models.&lt;/p&gt;&#xA;&lt;p&gt;The code below outlines every step required for 🤗 Optimum inference using ONNX Runtime with relevant comments:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-py&#34; data-lang=&#34;py&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;optimum.onnxruntime&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; ORTModelForQuestionAnswering&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;optimum.onnxruntime&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; ORTOptimizer&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;optimum.onnxruntime.configuration&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; OptimizationConfig&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;transformers&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; AutoTokenizer&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;model_name = &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;/workspace/distilroberta-base-distilsquad&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;tokenizer = AutoTokenizer.from_pretrained(model_name)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;# Load a model and export it to ONNX format&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;model = ORTModelForQuestionAnswering.from_pretrained(model_name, from_transformers=&lt;span style=&#34;font-weight:bold&#34;&gt;True&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;# Define optimization configuration&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;opt_config = OptimizationConfig(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    optimization_level=2,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    optimize_for_gpu=&lt;span style=&#34;font-weight:bold&#34;&gt;True&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fp16=&lt;span style=&#34;font-weight:bold&#34;&gt;True&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;# Create optimizer&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;optimizer = ORTOptimizer.from_pretrained(model)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;# Optimize the model applying defined optimization strategy&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;optimizer.optimize(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    optimization_config=opt_config,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    save_dir=&lt;span style=&#34;font-style:italic&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;{&lt;/span&gt;model_name&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;/onnx&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    file_suffix=&lt;span style=&#34;font-weight:bold&#34;&gt;None&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;# Load the optimized model for inference&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;model = ORTModelForQuestionAnswering.from_pretrained(model_name, file_name=&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;onnx/model.onnx&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;qa_eval(model=model, tokenizer=tokenizer)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;exact_match&amp;#39;&lt;/span&gt;: 81.7029, &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;f1&amp;#39;&lt;/span&gt;: 88.7575, &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;samples_per_second&amp;#39;&lt;/span&gt;: 477.7468}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The throughput of the optimized model improved by 24% compared to the initial distilled model.&lt;/p&gt;&#xA;&lt;p&gt;A few notes on the example configuration.&lt;/p&gt;&#xA;&lt;p&gt;Since our focus is on GPU inference, setting the &lt;code&gt;optimize_for_gpu&lt;/code&gt; to &lt;code&gt;True&lt;/code&gt; is crucial. There is no specific reason to go with the &lt;code&gt;optimization_level=2&lt;/code&gt; other than it being a good starting point. If mixed precision is enabled (&lt;code&gt;fp16=True&lt;/code&gt;), there is no need to use &lt;code&gt;autocast&lt;/code&gt; during inference. However, if the optimization configuration being used is unknown, it&amp;rsquo;s ok to use both &lt;code&gt;autocast&lt;/code&gt; and optimized models together.&lt;/p&gt;&#xA;&lt;h2 id=&#34;numerology&#34;&gt;Numerology&lt;/h2&gt;&#xA;&lt;p&gt;Ok, these are some of the optimization techniques that work well with 🤗 Transformers pipelines in my experience. As most of the examples are based on the distilled model, let&amp;rsquo;s see the results with the same techniques being applied to the baseline model too. The table can be mentally broken down into two parts&amp;mdash;the first part without distillation, and the second part containing optimizations combined with distillation.&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th style=&#34;text-align: left&#34;&gt;Approach&lt;/th&gt;&#xA;          &lt;th style=&#34;text-align: right&#34;&gt;Exact Match&lt;/th&gt;&#xA;          &lt;th style=&#34;text-align: right&#34;&gt;F1&lt;/th&gt;&#xA;          &lt;th style=&#34;text-align: right&#34;&gt;F1 diff&lt;/th&gt;&#xA;          &lt;th style=&#34;text-align: right&#34;&gt;Samples/sec&lt;/th&gt;&#xA;          &lt;th style=&#34;text-align: right&#34;&gt;Speedup&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td style=&#34;text-align: left&#34;&gt;Baseline&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;85.65&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;92.19&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;100%&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;293.4&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;1.00&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td style=&#34;text-align: left&#34;&gt;Automatic Mixed Precision (AMP)&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;85.64&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;92.19&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;100%&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;347.2&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;1.27&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td style=&#34;text-align: left&#34;&gt;TorchScript + AMP&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;85.64&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;92.19&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;100%&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;379.8&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;1.29&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td style=&#34;text-align: left&#34;&gt;Optimum (ORT)&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;&lt;strong&gt;85.7&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;&lt;strong&gt;92.21&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;100%&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;429.6&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;1.46&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td style=&#34;text-align: left&#34;&gt;Knowledge Distillation&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;81.67&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;88.74&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;96%&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;384.2&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;1.31&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td style=&#34;text-align: left&#34;&gt;Distillation + AMP&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;81.7&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;88.76&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;96%&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;416.5&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;1.42&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td style=&#34;text-align: left&#34;&gt;Distillation + TorchScript + AMP&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;81.7&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;88.76&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;96%&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;446.3&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;1.52&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td style=&#34;text-align: left&#34;&gt;Distillation + Optimum (ORT)&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;81.7&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;88.76&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;96%&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;&lt;strong&gt;477.7&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;1.63&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;Although the difference is negligible, using 🤗 Optimum with ONNX Runtime results in slightly higher accuracy compared to the baseline model. What&amp;rsquo;s even better is that all the described methods have no negative impact on accuracy, except for knowledge distillation. It looks like that, in order to surpass the 1.5x throughput speedup, one may have to sacrifice some accuracy. While this threshold is specific to the particular hardware, the general trend should be similar for different GPUs. So it&amp;rsquo;s not surprising that the highest throughput is achieved by using 🤗 Optimum in combination with knowledge distillation, as these are the two most significant contributing factors.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Adaptive Reranker Weight</title>
      <link>https://donderom.com/posts/adaptive-reranker-weight/</link>
      <pubDate>Sat, 15 Oct 2022 11:38:30 +0200</pubDate>
      <guid>https://donderom.com/posts/adaptive-reranker-weight/</guid>
      <description>&lt;p&gt;A method for improving the search results (e.g. for question answering retrieval) is known as &lt;em&gt;Retrieve &amp;amp; Re-rank&lt;/em&gt;. The Retrieve &amp;amp; Re-rank pipeline consists of at least two components: the retriever and the re-ranker. The retriever is responsible for retrieving a number of documents given a search query. The relevancy of the retrieved documents can be further improved by the re-ranker, which allows retrieving more documents than required for the next pipeline stage.&lt;/p&gt;&#xA;&lt;p&gt;Let&amp;rsquo;s see an example of the output at each stage.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-py&#34; data-lang=&#34;py&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;query = &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;what is re-ranking?&amp;#39;&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;# Search query&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;n = 10 &lt;span style=&#34;font-style:italic&#34;&gt;# Number of documents to retrieve&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;# Assume this function returns a list of documents with score&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docs = retrieve(query=query, n=n)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;scores = [doc[&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#39;score&amp;#39;&lt;/span&gt;] &lt;span style=&#34;font-weight:bold&#34;&gt;for&lt;/span&gt; doc &lt;span style=&#34;font-weight:bold&#34;&gt;in&lt;/span&gt; docs]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;# [0.9782995053726794,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;#  0.9504939500760989,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;#  0.8765814146070106,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;#  0.8623934128019434,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;#  0.842523354483268,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;#  0.7736853461402741,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;#  0.7713904667955406,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;#  0.6740331628686816,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;#  0.6378117863548827,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;#  0.5634670917387724]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;# And this function re-ranks the documents based on the&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;# relevancy to the query and returns the new scores&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;reranked = rerank(query=query, docs=docs)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;# [0.8958727100108653,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;#  0.9704265468563152,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;#  0.8037856351531634,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;#  0.4605732745735953,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;#  0.9991750843646917,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;#  0.7299899568668072,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;#  0.6836966943663378,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;#  0.6294383998509153,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;#  0.5605524792499585,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;#  0.41810846856511075]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, given two sets of scores per document, what is the total score? Calculating the average is one of the simplest approaches that could work.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-py&#34; data-lang=&#34;py&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;total_scores = [(s + r) / 2 &lt;span style=&#34;font-weight:bold&#34;&gt;for&lt;/span&gt; s, r &lt;span style=&#34;font-weight:bold&#34;&gt;in&lt;/span&gt; zip(scores, reranked)]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;# [0.9370861076917724,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;#  0.960460248466207,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;#  0.840183524880087,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;#  0.6614833436877694,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;#  0.9208492194239799,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;#  0.7518376515035406,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;#  0.7275435805809392,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;#  0.6517357813597985,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;#  0.5991821328024206,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;#  0.4907877801519416]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After combining both scores, the second document has the highest score now. If we want to assign different weights to the retriever and re-ranker scores, we can do so by multiplying the scores with their respective weight parameters:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-py&#34; data-lang=&#34;py&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;# The weight values are completely arbitrary&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;retriever_weight = 1.2&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;reranker_weight = 1.5 &lt;span style=&#34;font-style:italic&#34;&gt;# Boost reranker score a little bit&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;scoring = &lt;span style=&#34;font-weight:bold&#34;&gt;lambda&lt;/span&gt; s, r: (s * retriever_weight + r * reranker_weight) / 2&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;total_scores = [scoring(s, r) &lt;span style=&#34;font-weight:bold&#34;&gt;for&lt;/span&gt; s, r &lt;span style=&#34;font-weight:bold&#34;&gt;in&lt;/span&gt; zip(scores, reranked)]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now the question is, what value should be used for the re-ranker weight? Looking at the total scores, it appears that the positions of the first and second documents have been swapped, with the fifth document occupying third place and so on. There is nothing wrong with keeping the re-ranker weight static. But what if, instead swapping the first two places, the first document ends up becoming the last one? The same weight might not be sufficient for so many search queries and numbers of documents.&lt;/p&gt;&#xA;&lt;p&gt;&lt;span class=&#34;newthought&#34;&gt;The intuition&lt;/span&gt;&#xA; behind the idea is that the more spread out the documents are after re-ranking, the greater the weight. What makes it intuitive is that it&amp;rsquo;s about the positional difference, rather than the score. Although the total score difference might be insignificant, moving several positions down from the top can be detrimental to the pipeline performance. It&amp;rsquo;s not a big deal if we retrieve 10 documents and show/process them all. In case when we retrieve 100 documents to return top-5 of them, any document ranked higher than 5 will be filtered out.&lt;/p&gt;&#xA;&lt;p&gt;If we view the document positions before and after re-ranking as observed and predicted values, we can use any deviation or error function that is used to measure the difference between values. One such function is &lt;em&gt;Root Mean Square Error&lt;/em&gt;:&lt;/p&gt;&#xA;&lt;p&gt;$$RMSE = \sqrt{\frac{1}{n}\sum_{i=1}^{n}(y_{i}-x_{i})^{2}}$$&lt;/p&gt;&#xA;&lt;p&gt;Using the scores from above, it can be calculated as follows:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-py&#34; data-lang=&#34;py&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;numpy&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;as&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;np&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;retrieved_pos = np.argsort(scores)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;reranked_pos = np.argsort(reranked)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;np.sqrt(np.mean(np.square(reranked_pos - retrieved_pos)))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;# 2.23606797749979&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Another example is &lt;em&gt;Mean Absolute Error&lt;/em&gt;:&lt;/p&gt;&#xA;&lt;p&gt;$$MAE = \frac{1}{n}\sum_{i=1}^{n}|y_{i}-x_{i}|$$&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-py&#34; data-lang=&#34;py&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;np.mean(np.abs(reranked_pos - retrieved_pos))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;# 1.6&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Using different error functions will result in different values given the same magnitude of errors. In this case, the error of the MAE is smaller than the RMSE. We cannot compare the errors of different functions, but using the same magnitude of numbers allows us to choose any of them based on how large we want the resulting value to be.&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;Adaptive reranker weight&lt;/em&gt; is the measure between the retrieved and reranked positions. In other words, the adaptive weight is determined by how much the retrieved documents are rearranged by the re-ranker.&lt;/p&gt;&#xA;&lt;p&gt;$$ARW = max(e, w), \qquad \forall e,w\in\mathbb{R}$$&#xA;, where $e$ is error value and $w$ is minimum weight.&lt;/p&gt;&#xA;&lt;p&gt;With minimum weight set to zero, the adaptive reranker weight is just the error:&lt;/p&gt;&#xA;&lt;p&gt;$$ARW = max(RMSE, 0) = RMSE = \sqrt{\frac{1}{n}\sum_{i=1}^{n}(y_{i}-x_{i})^{2}}$$&lt;/p&gt;&#xA;&lt;p&gt;The adaptive reranker weight with MAE error and minimum weight of 1:&lt;/p&gt;&#xA;&lt;p&gt;$$ARW = max(\frac{1}{n}\sum_{i=1}^{n}|y_{i}-x_{i}|, 1)$$&lt;/p&gt;&#xA;&lt;p&gt;With scores changing per query, the re-ranker weight is adapted based on the difference between document positions. This approach can be further extended by incorporating the scores or by using different error functions. Logging the adaptive weight can also serve the purpose of monitoring the scoring deviations. The same idea can be applied down the pipeline to the components that calculate new sets of scores, thus affecting the document ranks.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Compositional streaming of services with FS2</title>
      <link>https://donderom.com/posts/compositional-streaming-of-services-with-fs2/</link>
      <pubDate>Sat, 23 May 2020 12:56:19 +0200</pubDate>
      <guid>https://donderom.com/posts/compositional-streaming-of-services-with-fs2/</guid>
      <description>&lt;p&gt;The intention of this post is to demonstrate a blueprint for composing multiple services as streams within the scope of one process with &lt;a href=&#34;https://fs2.io&#34;&gt;FS2&lt;/a&gt;. The idea is that, when dealing with unbounded I/O streams, modeling everything as streams leads to better composition.&lt;/p&gt;&#xA;&lt;p&gt;The libraries that will be used for all the examples:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;co.fs2&amp;#34;&lt;/span&gt; %% &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;fs2-core&amp;#34;&lt;/span&gt; % &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;2.3.0&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;// Configuration and validation&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;&lt;/span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;is.cir&amp;#34;&lt;/span&gt; %% &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;ciris-refined&amp;#34;&lt;/span&gt; % &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;1.0.4&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And the imports:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;cats.effect._&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;cats.implicits._&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;// Configuration and validation&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;&lt;/span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;ciris._&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;ciris.refined._&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;eu.timepit.refined.W&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;eu.timepit.refined.api.Refined&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;eu.timepit.refined.string.MatchesRegex&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;eu.timepit.refined.types.net.PortNumber&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;eu.timepit.refined.types.string.NonEmptyString&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;configuration&#34;&gt;Configuration&lt;/h2&gt;&#xA;&lt;p&gt;Let&amp;rsquo;s start with configuration. Unlike the rest of the examples, I&amp;rsquo;d like to pause and show the awesomeness of the &lt;a href=&#34;https://cir.is/&#34;&gt;Ciris&lt;/a&gt; configuration library. The docs cover most of its features, so let&amp;rsquo;s jump to defining a sample config for Elasticsearch. The basic config would probably contain the host and port number, at least. If it&amp;rsquo;s deployed on AWS, it could require the AWS region as well (along with many other parameters skipped for brevity). The region names follow a particular pattern so let&amp;rsquo;s start by validating it first:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;object&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;AWS&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Region&lt;/span&gt; = &lt;span style=&#34;font-weight:bold&#34;&gt;String&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Refined&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;MatchesRegex&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;W.`&amp;#34;([a-z]{2,}-)+[0-9]&amp;#34;`.T&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We&amp;rsquo;re using the &lt;a href=&#34;https://github.com/fthomas/refined&#34;&gt;refined&lt;/a&gt; library here to specify the &lt;code&gt;Region&lt;/code&gt; type, which checks if a string matches the region name regex.&lt;/p&gt;&#xA;&lt;p&gt;The full config will look like this:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;final&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;ElasticsearchConfig&lt;/span&gt;(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  host&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;NonEmptyString&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  port&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;PortNumber&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  region&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;AWS.Region&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Notice how every field is validated using special types from &lt;code&gt;refined&lt;/code&gt;. The &lt;code&gt;host&lt;/code&gt; field is a string but cannot be empty, while the &lt;code&gt;port&lt;/code&gt; number is guaranteed to be in the correct range by using the &lt;code&gt;PortNumber&lt;/code&gt; type. As a result, there are no &lt;code&gt;String&lt;/code&gt;s and &lt;code&gt;Int&lt;/code&gt;s all over the place with custom validation on top. Ok, it&amp;rsquo;s just a model, time to specify how to populate it with values.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;object&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;ElasticsearchConfig&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold&#34;&gt;val&lt;/span&gt; config&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;ConfigValue&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;ElasticsearchConfig&lt;/span&gt;] &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      env(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;ELASTICSEARCH_HOST&amp;#34;&lt;/span&gt;).default(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;localhost&amp;#34;&lt;/span&gt;).as[&lt;span style=&#34;&#34;&gt;NonEmptyString&lt;/span&gt;],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      env(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;ELASTICSEARCH_PORT&amp;#34;&lt;/span&gt;).as[&lt;span style=&#34;&#34;&gt;PortNumber&lt;/span&gt;].default(&lt;span style=&#34;font-weight:bold&#34;&gt;PortNumber&lt;/span&gt;(9200)),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      env(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;ELASTICSEARCH_REGION&amp;#34;&lt;/span&gt;).as[&lt;span style=&#34;&#34;&gt;AWS.Region&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ).parMapN(apply)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The values are loaded from environment variables with defaults, validated, and combined in parallel. The automatic support for &lt;code&gt;refined&lt;/code&gt; is provided by the  &lt;a href=&#34;https://cir.is/docs/modules#refined&#34;&gt;ciris-refined&lt;/a&gt; module. The same approach is used to compose multiple configurations into larger ones. For example given a separate Kafka config&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;final&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;KafkaConfig&lt;/span&gt;(bootstrapServers&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;NonEmptyString&lt;/span&gt;, ...)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;both configs can be accumulated into one main config:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;final&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Config&lt;/span&gt;(es&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;ElasticsearchConfig&lt;/span&gt;, kafka&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;KafkaConfig&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;object&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Config&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold&#34;&gt;val&lt;/span&gt; config&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;ConfigValue&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;Config&lt;/span&gt;] &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-weight:bold&#34;&gt;ElasticsearchConfig&lt;/span&gt;.config,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-weight:bold&#34;&gt;KafkaConfig&lt;/span&gt;.config&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ).parMapN(apply)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To load the configuration, we have to specify an effect type, which in this case will be cats-effect&amp;rsquo;s &lt;a href=&#34;https://typelevel.org/cats-effect/docs/2.x/datatypes/io&#34;&gt;&lt;code&gt;IO&lt;/code&gt;&lt;/a&gt; type:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;val&lt;/span&gt; config&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;IO&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;Config&lt;/span&gt;] &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Config&lt;/span&gt;.config.load[&lt;span style=&#34;&#34;&gt;IO&lt;/span&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;dependencies&#34;&gt;Dependencies&lt;/h2&gt;&#xA;&lt;p&gt;An initial stream to work with can be built by evaluating the &lt;code&gt;IO&lt;/code&gt; effect to emit the config value (effectively going from &lt;code&gt;IO[A]&lt;/code&gt; to &lt;code&gt;Stream[IO, A]&lt;/code&gt;):&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;val&lt;/span&gt; stream&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;fs2.Stream&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;IO&lt;/span&gt;, &lt;span style=&#34;&#34;&gt;Config&lt;/span&gt;] &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; fs2.&lt;span style=&#34;font-weight:bold&#34;&gt;Stream&lt;/span&gt;.eval(config)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can go back any time with &lt;code&gt;stream.compile.lastOrError: IO[Config]&lt;/code&gt;. The main reason not to, is to model an effectful computation as a stream, allowing it to be composed with a streaming service later on. In other words, instead of &amp;ldquo;wrapping&amp;rdquo; a dependency inside a stream, this modeling approach represents it as a stream itself.&lt;/p&gt;&#xA;&lt;p&gt;Another dependency is an Elasticsearch client. Here&amp;rsquo;s a complete example of the client interface with a dummy implementation:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;trait&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Event&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;trait&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;ElasticsearchClient&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;F&lt;/span&gt;[&lt;span style=&#34;font-weight:bold&#34;&gt;_&lt;/span&gt;]] {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold&#34;&gt;def&lt;/span&gt; index(event&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Event&lt;/span&gt;)&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;F&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;Unit&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;object&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;ElasticsearchClient&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold&#34;&gt;def&lt;/span&gt; create[&lt;span style=&#34;&#34;&gt;F&lt;/span&gt;[&lt;span style=&#34;font-weight:bold&#34;&gt;_&lt;/span&gt;]](config&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;ElasticsearchConfig&lt;/span&gt;)(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-weight:bold&#34;&gt;implicit&lt;/span&gt; F&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Sync&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;F&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  )&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;ElasticsearchClient&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;F&lt;/span&gt;] &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-weight:bold&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;ElasticsearchClient&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;F&lt;/span&gt;] {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;font-weight:bold&#34;&gt;def&lt;/span&gt; index(event&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Event&lt;/span&gt;)&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;F&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;Unit&lt;/span&gt;] &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        F.delay(println(&lt;span style=&#34;font-style:italic&#34;&gt;s&amp;#34;Indexing &lt;/span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;$event&lt;/span&gt;&lt;span style=&#34;font-style:italic&#34;&gt; to &lt;/span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;${&lt;/span&gt;config.host&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;${&lt;/span&gt;config.port&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The config is required to create a new client. There is already a stream emitting the config while evaluating the effect. The basic composition should be familiar:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;stream.map(config &lt;span style=&#34;font-weight:bold&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;ElasticsearchClient&lt;/span&gt;.create[&lt;span style=&#34;&#34;&gt;IO&lt;/span&gt;](config.es))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;// fs2.&lt;span style=&#34;font-weight:bold&#34;&gt;Stream&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;IO&lt;/span&gt;, &lt;span style=&#34;&#34;&gt;ElasticsearchClient&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;IO&lt;/span&gt;]] &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Stream&lt;/span&gt;(..)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Although it&amp;rsquo;s completely valid, the idea is to stick with modeling everything as streams. Additionally, the config could be needed downstream, making both the config and the client dependencies.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;val&lt;/span&gt; stream&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;fs2.Stream&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;IO&lt;/span&gt;, &lt;span style=&#34;&#34;&gt;ElasticsearchClient&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;IO&lt;/span&gt;]] &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;for&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  config &lt;span style=&#34;font-weight:bold&#34;&gt;&amp;lt;-&lt;/span&gt; fs2.&lt;span style=&#34;font-weight:bold&#34;&gt;Stream&lt;/span&gt;.eval(&lt;span style=&#34;font-weight:bold&#34;&gt;Config&lt;/span&gt;.config.load[&lt;span style=&#34;&#34;&gt;IO&lt;/span&gt;])&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  client &lt;span style=&#34;font-weight:bold&#34;&gt;&amp;lt;-&lt;/span&gt; fs2.&lt;span style=&#34;font-weight:bold&#34;&gt;Stream&lt;/span&gt;.emit(&lt;span style=&#34;font-weight:bold&#34;&gt;ElasticsearchClient&lt;/span&gt;.create[&lt;span style=&#34;&#34;&gt;IO&lt;/span&gt;](config.es))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} &lt;span style=&#34;font-weight:bold&#34;&gt;yield&lt;/span&gt; client&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;At this step, configuration loading and client creation emit one value, so they can remain in &lt;code&gt;IO&lt;/code&gt; without any problem. The composition would be similar. When FS2 shines, though, is in dealing with I/O computations in constant memory.&lt;/p&gt;&#xA;&lt;h2 id=&#34;composition&#34;&gt;Composition&lt;/h2&gt;&#xA;&lt;p&gt;It&amp;rsquo;s common to run both an HTTP server and a Kafka (or any other) consumer concurrently within the scope of one microservice. Continuing this example, an HTTP server could serve data from an Elasticsearch index, while a consumer would index data from a Kafka topic(s)&lt;em&gt;&lt;small&gt; (The code will be more abstract but two good libraries with FS2 support are &lt;a href=&#34;https://http4s.org/&#34;&gt;http4s&lt;/a&gt; and &lt;a href=&#34;https://fd4s.github.io/fs2-kafka/&#34;&gt;fs2-kafka&lt;/a&gt; respectively.)&lt;/small&gt;&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;A hypothetical API for a consumer that requires its own config and an Elasticsearch client producing a stream:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;def&lt;/span&gt; eventStream[&lt;span style=&#34;&#34;&gt;F&lt;/span&gt;[&lt;span style=&#34;font-weight:bold&#34;&gt;_&lt;/span&gt;]](config&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;KafkaConfig&lt;/span&gt;, client&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;ElasticsearchClient&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;F&lt;/span&gt;])(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold&#34;&gt;implicit&lt;/span&gt; F&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;ConcurrentEffect&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;F&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;fs2.Stream&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;F&lt;/span&gt;, &lt;span style=&#34;&#34;&gt;Unit&lt;/span&gt;] &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; ???&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And an HTTP server API that is modeled as cats-effect&amp;rsquo;s &lt;a href=&#34;https://typelevel.org/cats-effect/docs/2.x/datatypes/resource&#34;&gt;&lt;code&gt;Resource&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;trait&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Server&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;F&lt;/span&gt;[&lt;span style=&#34;font-weight:bold&#34;&gt;_&lt;/span&gt;]]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;def&lt;/span&gt; server[&lt;span style=&#34;&#34;&gt;F&lt;/span&gt;[&lt;span style=&#34;font-weight:bold&#34;&gt;_&lt;/span&gt;]](&lt;span style=&#34;font-weight:bold&#34;&gt;implicit&lt;/span&gt; F&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;ConcurrentEffect&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;F&lt;/span&gt;])&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Resource&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;F&lt;/span&gt;, &lt;span style=&#34;&#34;&gt;Server&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;F&lt;/span&gt;]] &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; ???&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Abstracting over effect type (all these &lt;code&gt;F[_]&lt;/code&gt;) following the least powerful typeclass rule is crucial for stream composition and testing. There is no need to constrain an effect with &lt;code&gt;ConcurrentEffect&lt;/code&gt; when &lt;code&gt;Sync&lt;/code&gt; or &lt;code&gt;Applicative&lt;/code&gt; would be enough.&lt;/p&gt;&#xA;&lt;p&gt;Given that the consumer and the server are independent services, they cannot be composed like dependencies, as they are supposed to run &lt;em&gt;concurrently&lt;/em&gt;. Instead of &amp;ldquo;sequential&amp;rdquo; &lt;code&gt;flatMap&lt;/code&gt;, we can use &lt;code&gt;parJoin&lt;/code&gt;, which will nondeterministically merge a stream of streams into a single stream. To run multiple streams concurrently with FS2:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;val&lt;/span&gt; stream&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;fs2.Stream&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;IO&lt;/span&gt;, &lt;span style=&#34;&#34;&gt;Unit&lt;/span&gt;] &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;for&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  config &lt;span style=&#34;font-weight:bold&#34;&gt;&amp;lt;-&lt;/span&gt; fs2.&lt;span style=&#34;font-weight:bold&#34;&gt;Stream&lt;/span&gt;.eval(&lt;span style=&#34;font-weight:bold&#34;&gt;Config&lt;/span&gt;.config.load[&lt;span style=&#34;&#34;&gt;IO&lt;/span&gt;])&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  client &lt;span style=&#34;font-weight:bold&#34;&gt;&amp;lt;-&lt;/span&gt; fs2.&lt;span style=&#34;font-weight:bold&#34;&gt;Stream&lt;/span&gt;.emit(&lt;span style=&#34;font-weight:bold&#34;&gt;ElasticsearchClient&lt;/span&gt;.create[&lt;span style=&#34;&#34;&gt;IO&lt;/span&gt;](config.es))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  app &lt;span style=&#34;font-weight:bold&#34;&gt;&amp;lt;-&lt;/span&gt; fs2.&lt;span style=&#34;font-weight:bold&#34;&gt;Stream&lt;/span&gt;(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    eventStream(config.kafka, client),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fs2.&lt;span style=&#34;font-weight:bold&#34;&gt;Stream&lt;/span&gt;.resource(server[&lt;span style=&#34;&#34;&gt;IO&lt;/span&gt;]) &lt;span style=&#34;font-style:italic&#34;&gt;// Resource[F, Server[F]] → Stream[F, Server[F]]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;&lt;/span&gt;  ).parJoinUnbounded&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} &lt;span style=&#34;font-weight:bold&#34;&gt;yield&lt;/span&gt; app&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And that&amp;rsquo;s it! More information about &lt;code&gt;parJoin&lt;/code&gt; and &lt;code&gt;parJoinUnbounded&lt;/code&gt;&lt;em&gt;&lt;small&gt; (For this example &lt;code&gt;parJoin(2)&lt;/code&gt; and &lt;code&gt;parJoinUnbounded&lt;/code&gt; are interchangeable but when adding one more service it&amp;rsquo;s easy to forget to bump the &lt;code&gt;maxOpen&lt;/code&gt; arguments of &lt;code&gt;parJoin&lt;/code&gt;.)&lt;/small&gt;&lt;/em&gt;, as well as many other composition primitives such as &lt;code&gt;concurrently&lt;/code&gt; (when the services depend on each other), can be found &lt;a href=&#34;https://s01.oss.sonatype.org/service/local/repositories/releases/archive/co/fs2/fs2-core_2.12/2.3.0/fs2-core_2.12-2.3.0-javadoc.jar/!/fs2/Stream.html&#34;&gt;here&lt;/a&gt;. The complete snippet of the main entry point with basic error handling would look like this:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;object&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Main&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;extends&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;IOApp&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold&#34;&gt;def&lt;/span&gt; run(args&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;List&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;String&lt;/span&gt;])&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;IO&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;ExitCode&lt;/span&gt;] &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-weight:bold&#34;&gt;val&lt;/span&gt; stream&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;fs2.Stream&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;IO&lt;/span&gt;, &lt;span style=&#34;&#34;&gt;Unit&lt;/span&gt;] &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;for&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      config &lt;span style=&#34;font-weight:bold&#34;&gt;&amp;lt;-&lt;/span&gt; fs2.&lt;span style=&#34;font-weight:bold&#34;&gt;Stream&lt;/span&gt;.eval(&lt;span style=&#34;font-weight:bold&#34;&gt;Config&lt;/span&gt;.config.load[&lt;span style=&#34;&#34;&gt;IO&lt;/span&gt;])&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      client &lt;span style=&#34;font-weight:bold&#34;&gt;&amp;lt;-&lt;/span&gt; fs2.&lt;span style=&#34;font-weight:bold&#34;&gt;Stream&lt;/span&gt;.emit(&lt;span style=&#34;font-weight:bold&#34;&gt;ElasticsearchClient&lt;/span&gt;.create[&lt;span style=&#34;&#34;&gt;IO&lt;/span&gt;](config.es))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      app &lt;span style=&#34;font-weight:bold&#34;&gt;&amp;lt;-&lt;/span&gt; fs2.&lt;span style=&#34;font-weight:bold&#34;&gt;Stream&lt;/span&gt;(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        eventStream(config.kafka, client),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        fs2.&lt;span style=&#34;font-weight:bold&#34;&gt;Stream&lt;/span&gt;.resource(server[&lt;span style=&#34;&#34;&gt;IO&lt;/span&gt;])&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      ).parJoinUnbounded&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    } &lt;span style=&#34;font-weight:bold&#34;&gt;yield&lt;/span&gt; app&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    stream&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      .compile&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      .drain&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      .onError {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-weight:bold&#34;&gt;case&lt;/span&gt; e&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Throwable&lt;/span&gt; =&amp;gt; &lt;span style=&#34;font-weight:bold&#34;&gt;IO&lt;/span&gt;(println(e.getMessage))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      .as(&lt;span style=&#34;font-weight:bold&#34;&gt;ExitCode&lt;/span&gt;.&lt;span style=&#34;font-weight:bold&#34;&gt;Success&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-style:italic&#34;&gt;// ...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;&lt;/span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Not only is the flow concise, but every line packs a punch. The part with &lt;code&gt;eval&lt;/code&gt; includes type-safe configuration loading semantics, performs it in parallel with validation, and evaluates an effect. Then, any value can be represented as a stream, be it effectful (with &lt;code&gt;eval&lt;/code&gt;) or not (with &lt;code&gt;emit&lt;/code&gt;, where a sequence of values is handled with &lt;code&gt;emits&lt;/code&gt;). The main part comprises an unbounded stream of messages and a server with built-in resource safety (&lt;code&gt;resource&lt;/code&gt;) that run concurrently (&lt;code&gt;parJoin&lt;/code&gt;, where service independence can be controlled by changing the concurrency operation). Adding a third service to the example (let say a message producer along with the consumer) won&amp;rsquo;t change the structure at all. To finish things off, there is a centralized place for error handling and straightforward support for testing. Mocking every dependency and service in unit tests can be avoided by replacing the &lt;code&gt;IO&lt;/code&gt; effect with another. Streaming semantics are already tested by the FS2 library itself, which allows focusing on service logic.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;em&gt;Why not use compositional streaming modeling for applications with streaming core?&lt;/em&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;</description>
    </item>
    <item>
      <title>Scaling effects in Scala</title>
      <link>https://donderom.com/posts/scaling-effects-in-scala/</link>
      <pubDate>Sun, 10 Feb 2019 15:32:58 +0200</pubDate>
      <guid>https://donderom.com/posts/scaling-effects-in-scala/</guid>
      <description>&lt;p&gt;42. A value. Different from 37 but both are numbers.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;&lt;em&gt;Effect&lt;/em&gt; is to type what type is to value.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;🄰s type allows to abstract over value, effect allows to abstract over type.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;42&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;text&amp;#34;&lt;/span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;String&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;def&lt;/span&gt; opt[&lt;span style=&#34;&#34;&gt;A&lt;/span&gt;](value&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;A&lt;/span&gt;)&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Option&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;A&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;opt(42)&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Option&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;Int&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;opt(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;text&amp;#34;&lt;/span&gt;)&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Option&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;String&lt;/span&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There are two types of effect scaling&amp;mdash;horizontal ⇄ and vertical ⇅.&lt;/p&gt;&#xA;&lt;h2 id=&#34;horizontal-scaling&#34;&gt;Horizontal Scaling&lt;/h2&gt;&#xA;&lt;p&gt;The simplest scaling approach that replaces an effect with another one.&lt;/p&gt;&#xA;&lt;p&gt;Built-in effects: &lt;code&gt;Option[A] → Future[A]&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Third-party effects: &lt;code&gt;Future[A] → Task[A]&lt;/code&gt;. &lt;em&gt;&lt;small&gt; (&lt;a href=&#34;https://monix.io&#34;&gt;https://monix.io&lt;/a&gt;)&lt;/small&gt;&lt;/em&gt;&#xA;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;scala.concurrent.Future&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;monix.eval.Task&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;monix.execution.Scheduler.Implicits.global&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;val&lt;/span&gt; future &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Future&lt;/span&gt;(42)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;val&lt;/span&gt; task &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Task&lt;/span&gt;.deferFuture(future) &lt;span style=&#34;font-style:italic&#34;&gt;// Future[A] → Task[A]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;&lt;/span&gt;task.runToFuture&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Future&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;Int&lt;/span&gt;]       &lt;span style=&#34;font-style:italic&#34;&gt;// Task[A] → Future[A]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;val&lt;/span&gt; io &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; task.toIO                  &lt;span style=&#34;font-style:italic&#34;&gt;// Task[A] → IO[A]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;&lt;/span&gt;io.to[&lt;span style=&#34;&#34;&gt;Task&lt;/span&gt;]&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Task&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;Int&lt;/span&gt;]              &lt;span style=&#34;font-style:italic&#34;&gt;// IO[A] → Task[A]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;// Future[A] → Task[A] → IO[A] → Task[A] → Future[A]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;&lt;/span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;Task&lt;/span&gt;.fromFuture(&lt;span style=&#34;font-weight:bold&#34;&gt;Future&lt;/span&gt;(42)).toIO.to[&lt;span style=&#34;&#34;&gt;Task&lt;/span&gt;].runToFuture&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Future&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;Int&lt;/span&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Horizontal scaling is bidirectional if types are isomorphic, and unidirectional otherwise.&lt;/p&gt;&#xA;&lt;h2 id=&#34;vertical-scaling&#34;&gt;Vertical Scaling&lt;/h2&gt;&#xA;&lt;p&gt;⼐ With ability to abstract over types, the next step is to abstract over effects.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;&lt;em&gt;Algebra&lt;/em&gt; is to effect what effect is to type.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;One way to abstract over effects is the &lt;em&gt;tagless final&lt;/em&gt; encoding.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;java.util.UUID&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;final&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Snapshot&lt;/span&gt;(tenantId&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;UUID&lt;/span&gt;, id&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;UUID&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;trait&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;SnapshotRepo&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;F&lt;/span&gt;[&lt;span style=&#34;font-weight:bold&#34;&gt;_&lt;/span&gt;]] {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold&#34;&gt;def&lt;/span&gt; current(tenantId&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;UUID&lt;/span&gt;)&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;F&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;Option&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;Snapshot&lt;/span&gt;]]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Another way is using &lt;em&gt;Free monads&lt;/em&gt;. &lt;em&gt;&lt;small&gt; (&lt;a href=&#34;https://typelevel.org/cats/datatypes/freemonad.html&#34;&gt;https://typelevel.org/cats/datatypes/freemonad.html&lt;/a&gt;)&lt;/small&gt;&lt;/em&gt;&#xA;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;cats.&lt;/span&gt;~&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;cats.free.Free&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;sealed&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;trait&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;ObjectStoreAlgebra&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;A&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;final&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Put&lt;/span&gt;(path&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;String&lt;/span&gt;, bytes&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Array&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;Byte&lt;/span&gt;]) &lt;span style=&#34;font-weight:bold&#34;&gt;extends&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;ObjectStoreAlgebra&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;Unit&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;object&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;ObjectStore&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;&#34;&gt;ObjectStore&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;A&lt;/span&gt;] &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Free&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;ObjectStoreAlgebra&lt;/span&gt;, &lt;span style=&#34;&#34;&gt;A&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold&#34;&gt;def&lt;/span&gt; put(path&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;String&lt;/span&gt;, bytes&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Array&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;Byte&lt;/span&gt;])&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;ObjectStore&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;Unit&lt;/span&gt;] &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-weight:bold&#34;&gt;Free&lt;/span&gt;.liftF[&lt;span style=&#34;&#34;&gt;ObjectStoreAlgebra&lt;/span&gt;, &lt;span style=&#34;&#34;&gt;Unit&lt;/span&gt;](&lt;span style=&#34;font-weight:bold&#34;&gt;Put&lt;/span&gt;(path, bytes))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;def&lt;/span&gt; compiler[&lt;span style=&#34;&#34;&gt;F&lt;/span&gt;[&lt;span style=&#34;font-weight:bold&#34;&gt;_&lt;/span&gt;]]&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;ObjectStoreAlgebra&lt;/span&gt; &lt;span style=&#34;&#34;&gt;~&amp;gt;&lt;/span&gt; &lt;span style=&#34;&#34;&gt;F&lt;/span&gt; = ???&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;⇄ Algebras can be scaled horizontally by replacing one approach with another.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;&lt;em&gt;Extensible effects&lt;/em&gt; are to algebras what algebras are to effects.&lt;/strong&gt; &lt;em&gt;&lt;small&gt; (&lt;a href=&#34;https://atnos-org.github.io/eff&#34;&gt;https://atnos-org.github.io/eff&lt;/a&gt;)&lt;/small&gt;&lt;/em&gt;&#xA;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;cats.Monad&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;cats.data.Writer&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;org.atnos.eff._&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;org.atnos.eff.all._&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;sealed&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;trait&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Event&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;final&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;ObjectStored&lt;/span&gt;(path&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;String&lt;/span&gt;) &lt;span style=&#34;font-weight:bold&#34;&gt;extends&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Event&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;trait&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;PayloadAlgebra&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;F&lt;/span&gt;[&lt;span style=&#34;font-weight:bold&#34;&gt;_&lt;/span&gt;]] {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;_&lt;/span&gt;&lt;span style=&#34;&#34;&gt;effect&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;R&lt;/span&gt;] &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; F |= R                    &lt;span style=&#34;font-style:italic&#34;&gt;// Expect to deal with F effect&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;font-weight:bold&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;_&lt;/span&gt;&lt;span style=&#34;&#34;&gt;writer&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;R&lt;/span&gt;] &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Writer&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;String&lt;/span&gt;, &lt;span style=&#34;&#34;&gt;?&lt;/span&gt;] |= R    &lt;span style=&#34;font-style:italic&#34;&gt;// And the Cats&amp;#39; Writer for logging&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold&#34;&gt;def&lt;/span&gt; process[&lt;span style=&#34;&#34;&gt;R:&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;_&lt;/span&gt;&lt;span style=&#34;&#34;&gt;option:&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;_&lt;/span&gt;&lt;span style=&#34;&#34;&gt;effect:&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;_&lt;/span&gt;&lt;span style=&#34;&#34;&gt;writer&lt;/span&gt;](  &lt;span style=&#34;font-style:italic&#34;&gt;// Expect to see some Options&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;&lt;/span&gt;    tenantId&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt;     &lt;span style=&#34;&#34;&gt;UUID&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    payload&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt;      &lt;span style=&#34;&#34;&gt;Array&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;Byte&lt;/span&gt;],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    snapshotRepo&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;SnapshotRepo&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;F&lt;/span&gt;],            &lt;span style=&#34;font-style:italic&#34;&gt;// Pass tagless final interpreter&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;&lt;/span&gt;    compiler&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt;     &lt;span style=&#34;&#34;&gt;ObjectStoreAlgebra&lt;/span&gt; &lt;span style=&#34;&#34;&gt;~&amp;gt;&lt;/span&gt; &lt;span style=&#34;&#34;&gt;F&lt;/span&gt;     &lt;span style=&#34;font-style:italic&#34;&gt;// Pass free monad compiler&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;&lt;/span&gt;  )(&lt;span style=&#34;font-weight:bold&#34;&gt;implicit&lt;/span&gt; M&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Monad&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;F&lt;/span&gt;])&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Eff&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;R&lt;/span&gt;, &lt;span style=&#34;&#34;&gt;Event&lt;/span&gt;] &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;for&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-weight:bold&#34;&gt;_&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;←&lt;/span&gt; tell(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;Starting processing&amp;#34;&lt;/span&gt;)                                 &lt;span style=&#34;font-style:italic&#34;&gt;// Cats data type&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;&lt;/span&gt;    maybeSnapshot &lt;span style=&#34;font-weight:bold&#34;&gt;←&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Eff&lt;/span&gt;.send(snapshotRepo.current(tenantId))        &lt;span style=&#34;font-style:italic&#34;&gt;// Tagless final algebra effect&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;&lt;/span&gt;    snapshot &lt;span style=&#34;font-weight:bold&#34;&gt;←&lt;/span&gt; fromOption(maybeSnapshot)                            &lt;span style=&#34;font-style:italic&#34;&gt;// Built-in Option type&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;&lt;/span&gt;    path &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;s&amp;#34;&lt;/span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;${&lt;/span&gt;tenantId&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;${&lt;/span&gt;snapshot.id&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;&lt;/span&gt;                            &lt;span style=&#34;font-style:italic&#34;&gt;// A value&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;font-weight:bold&#34;&gt;_&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;←&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Eff&lt;/span&gt;.send(&lt;span style=&#34;font-weight:bold&#34;&gt;ObjectStore&lt;/span&gt;.put(path, payload).foldMap(compiler))  &lt;span style=&#34;font-style:italic&#34;&gt;// Free monad effect&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;&lt;/span&gt;  } &lt;span style=&#34;font-weight:bold&#34;&gt;yield&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;ObjectStored&lt;/span&gt;(path)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;⇅ Vertical scaling implies both scaling up and scaling down.&lt;/p&gt;&#xA;&lt;p&gt;☰ Different effects, along with algebra interpreters, are computed in one place. &lt;em&gt;&lt;small&gt; (&lt;a href=&#34;https://typelevel.org/cats/datatypes/writer.html&#34;&gt;https://typelevel.org/cats/datatypes/writer.html&lt;/a&gt;)&lt;/small&gt;&lt;/em&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;↓ The API is scaled down being encoded as tagless final &lt;code&gt;trait PayloadAlgebra[F[_]]&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;↓ Further downscaling is achieved by running the computation.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Stack&lt;/span&gt; = &lt;span style=&#34;font-weight:bold&#34;&gt;Fx&lt;/span&gt;.fx3[&lt;span style=&#34;&#34;&gt;Option&lt;/span&gt;, &lt;span style=&#34;&#34;&gt;Task&lt;/span&gt;, &lt;span style=&#34;&#34;&gt;Writer&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;String&lt;/span&gt;, &lt;span style=&#34;&#34;&gt;?&lt;/span&gt;]]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;val&lt;/span&gt; algebra &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;PayloadAlgebra&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;Task&lt;/span&gt;] {}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;val&lt;/span&gt; computation&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Task&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;Option&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;Event&lt;/span&gt;]] &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; algebra&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  .process[&lt;span style=&#34;&#34;&gt;Stack&lt;/span&gt;](tenantId, payload, snapshotRepo, compiler)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  .runOption&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  .runWriterUnsafe[&lt;span style=&#34;&#34;&gt;String&lt;/span&gt;](println)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  .runAsync&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;⧇ Using the abstract interface, the computation results in nested effects.&lt;/p&gt;&#xA;&lt;p&gt;⟷ The effect can be scaled horizontally as at the beginning.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;  &lt;a href=&#34;https://donderom.com/posts/scaling-effects-in-scala/&#34;&gt;View the diagrams on the website&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;↻&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Identify tables to cache with Spark SQL Analyzer</title>
      <link>https://donderom.com/posts/identify-tables-to-cache-with-spark-sql-analyzer/</link>
      <pubDate>Wed, 28 Feb 2018 19:56:54 +0300</pubDate>
      <guid>https://donderom.com/posts/identify-tables-to-cache-with-spark-sql-analyzer/</guid>
      <description>&lt;p&gt;Consider having dozens of Cassandra tables as data sources/sinks, with Spark SQL transformations in between. Sometimes, loading the whole table or a data subset by multiple keys is inevitable. Every optimization applied to the underlying data source and intermediary dataframes goes a long way. One of such optimizations is caching. As the number of transformations grows, the DAG can become unreadable very quickly. It would be nice to be able to check if there are any candidates for caching without having to dig deep into the code. Let&amp;rsquo;s take a look at some examples.&lt;/p&gt;&#xA;&lt;h2 id=&#34;use-cases&#34;&gt;Use Cases&lt;/h2&gt;&#xA;&lt;p&gt;Basically, any table that is used more than once can be a candidate for caching. Let&amp;rsquo;s create a test table named &lt;code&gt;t1&lt;/code&gt; (all the code is tested on Spark 2.3.0):&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;spark.sql(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;select 1&amp;#34;&lt;/span&gt;).createOrReplaceTempView(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;t1&amp;#34;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now let&amp;rsquo;s create another table that is based on &lt;code&gt;t1&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;spark.sql(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;select * from t1&amp;#34;&lt;/span&gt;).createOrReplaceTempView(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;t2&amp;#34;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When a Spark action is applied to &lt;code&gt;t2&lt;/code&gt; table, &lt;code&gt;t1&lt;/code&gt; is read only once, so it should be fine. What if we create yet another table that is using &lt;code&gt;t1&lt;/code&gt;?&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;spark.sql(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;select * from t1&amp;#34;&lt;/span&gt;).createOrReplaceTempView(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;t3&amp;#34;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;t1&lt;/code&gt; is now used twice, by both &lt;code&gt;t2&lt;/code&gt; and &lt;code&gt;t3&lt;/code&gt;, so it could potentially benefit from caching. The pitfall is that to determine this, all of the transformations should be analyzed.&lt;/p&gt;&#xA;&lt;p&gt;Let&amp;rsquo;s create yet another transformation that does not require caching:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;spark.sql(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;select 2&amp;#34;&lt;/span&gt;).createOrReplaceTempView(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;t4&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;spark.sql(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;select * from t4&amp;#34;&lt;/span&gt;).createOrReplaceTempView(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;t5&amp;#34;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There are some corner cases that would be nice to avoid. In the following example&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;spark.sql(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;select * from (select * from t5) as t1&amp;#34;&lt;/span&gt;).createOrReplaceTempView(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;t6&amp;#34;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;t1&lt;/code&gt; is an alias, but there is a table with exactly the same name. Therefore,  it should not be mistaken for the actual table.&lt;/p&gt;&#xA;&lt;p&gt;If a table is already cached, it should be excluded from consideration:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;spark.sql(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;select 3&amp;#34;&lt;/span&gt;).createOrReplaceTempView(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;t7&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;spark.table(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;t7&amp;#34;&lt;/span&gt;).cache()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;spark.sql(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;select * from t7&amp;#34;&lt;/span&gt;).createOrReplaceTempView(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;t8&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;spark.sql(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;select * from t7&amp;#34;&lt;/span&gt;).createOrReplaceTempView(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;t9&amp;#34;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;in-action&#34;&gt;In Action&lt;/h2&gt;&#xA;&lt;p&gt;Following is the code covering the aforementioned use cases:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;org.apache.spark.sql.SparkSession&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;org.apache.spark.sql.catalyst.plans.logical.LogicalPlan&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;org.apache.spark.sql.catalyst.plans.logical.SubqueryAlias&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;object&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Analyzer&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;&#34;&gt;PlanCache&lt;/span&gt; = &lt;span style=&#34;font-weight:bold&#34;&gt;Map&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;String&lt;/span&gt;, &lt;span style=&#34;&#34;&gt;LogicalPlan&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Result&lt;/span&gt;(parents&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Map&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;String&lt;/span&gt;, &lt;span style=&#34;&#34;&gt;List&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;String&lt;/span&gt;]], planCache&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;PlanCache&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold&#34;&gt;def&lt;/span&gt; parents(&lt;span style=&#34;font-weight:bold&#34;&gt;implicit&lt;/span&gt; spark&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;SparkSession&lt;/span&gt;)&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Map&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;String&lt;/span&gt;, &lt;span style=&#34;&#34;&gt;List&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;String&lt;/span&gt;]] &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-weight:bold&#34;&gt;def&lt;/span&gt; updateCache(table&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;String&lt;/span&gt;, cache&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;PlanCache&lt;/span&gt;)&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;PlanCache&lt;/span&gt; =&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;font-weight:bold&#34;&gt;if&lt;/span&gt; (cache.contains(table)) cache &lt;span style=&#34;font-weight:bold&#34;&gt;else&lt;/span&gt; cache + (table -&amp;gt; analyzedPlan(table))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-weight:bold&#34;&gt;def&lt;/span&gt; subquery(plan&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;SubqueryAlias&lt;/span&gt;)&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Boolean&lt;/span&gt; =&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      plan.child.nodeName != &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;SubqueryAlias&amp;#34;&lt;/span&gt; &amp;amp;&amp;amp;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      spark.catalog.tableExists(plan.alias) &amp;amp;&amp;amp;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      !spark.catalog.isCached(plan.alias)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    @annotation.tailrec&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-weight:bold&#34;&gt;def&lt;/span&gt; parentAliases(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      nodes&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Seq&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;LogicalPlan&lt;/span&gt;],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      names&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;List&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;String&lt;/span&gt;],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      planCache&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;PlanCache&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; (&lt;span style=&#34;&#34;&gt;List&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;String&lt;/span&gt;], &lt;span style=&#34;font-weight:bold&#34;&gt;PlanCache&lt;/span&gt;) &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; nodes &lt;span style=&#34;font-weight:bold&#34;&gt;match&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;font-weight:bold&#34;&gt;case&lt;/span&gt; (plan &lt;span style=&#34;font-weight:bold&#34;&gt;@&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;SubqueryAlias&lt;/span&gt;(alias, child)) +: plans &lt;span style=&#34;font-weight:bold&#34;&gt;if&lt;/span&gt; subquery(plan) &lt;span style=&#34;font-weight:bold&#34;&gt;=&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-weight:bold&#34;&gt;val&lt;/span&gt; newCache &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; updateCache(alias, planCache)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-weight:bold&#34;&gt;if&lt;/span&gt; (plan.sameResult(newCache(alias))) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          parentAliases(plans, alias &lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;names&lt;/span&gt;, newCache)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        } &lt;span style=&#34;font-weight:bold&#34;&gt;else&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          parentAliases(plans ++ subplans(plan), names, planCache)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;font-weight:bold&#34;&gt;case&lt;/span&gt; plan +: plans &lt;span style=&#34;font-weight:bold&#34;&gt;=&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        parentAliases(plans ++ subplans(plan), names, planCache)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;font-weight:bold&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;_&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;=&amp;gt;&lt;/span&gt; (names, planCache)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-weight:bold&#34;&gt;val&lt;/span&gt; tables &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; spark.catalog.listTables.collect&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    tables.foldLeft(&lt;span style=&#34;font-weight:bold&#34;&gt;Result&lt;/span&gt;(&lt;span style=&#34;font-weight:bold&#34;&gt;Map&lt;/span&gt;(), &lt;span style=&#34;font-weight:bold&#34;&gt;Map&lt;/span&gt;())) { &lt;span style=&#34;font-weight:bold&#34;&gt;case&lt;/span&gt; (result, table) &lt;span style=&#34;font-weight:bold&#34;&gt;=&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;font-weight:bold&#34;&gt;val&lt;/span&gt; cache &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; updateCache(table.name, result.planCache)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;font-weight:bold&#34;&gt;val&lt;/span&gt; nodes &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; cache(table.name).children&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;font-weight:bold&#34;&gt;val&lt;/span&gt; (aliases, newCache) &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; parentAliases(nodes, &lt;span style=&#34;font-weight:bold&#34;&gt;Nil&lt;/span&gt;, cache)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;font-weight:bold&#34;&gt;val&lt;/span&gt; parents &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; aliases.foldLeft(result.parents) { &lt;span style=&#34;font-weight:bold&#34;&gt;case&lt;/span&gt; (parents, alias) &lt;span style=&#34;font-weight:bold&#34;&gt;=&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-weight:bold&#34;&gt;val&lt;/span&gt; children &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; table.name &lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;parents.getOrElse&lt;/span&gt;(&lt;span style=&#34;&#34;&gt;alias&lt;/span&gt;, &lt;span style=&#34;&#34;&gt;Nil&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        parents + (alias -&amp;gt; children)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;font-weight:bold&#34;&gt;Result&lt;/span&gt;(parents, newCache)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }.parents&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold&#34;&gt;def&lt;/span&gt; analyzedPlan(table&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;String&lt;/span&gt;)(&lt;span style=&#34;font-weight:bold&#34;&gt;implicit&lt;/span&gt; spark&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;SparkSession&lt;/span&gt;)&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;LogicalPlan&lt;/span&gt; =&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    spark.table(table).queryExecution.analyzed&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold&#34;&gt;def&lt;/span&gt; subplans(plan&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;LogicalPlan&lt;/span&gt;)&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Seq&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;LogicalPlan&lt;/span&gt;] &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    plan.children ++ plan.subqueries&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold&#34;&gt;def&lt;/span&gt; showReused(&lt;span style=&#34;font-weight:bold&#34;&gt;implicit&lt;/span&gt; spark&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;SparkSession&lt;/span&gt;)&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Unit&lt;/span&gt; =&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    parents.foreach { &lt;span style=&#34;font-weight:bold&#34;&gt;case&lt;/span&gt; (parent, children) &lt;span style=&#34;font-weight:bold&#34;&gt;=&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;font-weight:bold&#34;&gt;if&lt;/span&gt; (children.size &amp;gt; 1)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        println(&lt;span style=&#34;font-style:italic&#34;&gt;s&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;$parent&lt;/span&gt;&lt;span style=&#34;font-style:italic&#34;&gt; is used by &lt;/span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;${&lt;/span&gt;children.mkString(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;, &amp;#34;&lt;/span&gt;)&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To ensure efficiency, the code has been optimized to process every registered table only once. It is worth noting that most of the &lt;em&gt;cache&lt;/em&gt; references in the code refer to the internal cache rather than the Spark cache. The idea is this:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;The analyzed logical plan generated by Spark SQL Analyzer is acquired for every table &lt;code&gt;spark.table(table).queryExecution.analyzed&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;li&gt;The child nodes of a query plan are processed recursively to search for &lt;code&gt;SubqueryAlias&lt;/code&gt; nodes.&lt;/li&gt;&#xA;&lt;li&gt;When a &lt;code&gt;SubqueryAlias&lt;/code&gt; node is found, checks are performed to verify that the table exists, is not cached, is not an alias, etc.&lt;/li&gt;&#xA;&lt;li&gt;The goal is to collect all the tables that are used by any other table in order to identify common parent tables.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;If we run the code&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;scala&amp;gt; Analyzer.showReused(spark)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;t1 is used by t3, t2&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;we can see that:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;t5&lt;/code&gt; is not a valid candidate and is therefore missing from the results, as expected;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;t6&lt;/code&gt;, which uses an alias, is also not included in the results;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;t7&lt;/code&gt; is already cached, so there is no need to consider it for caching.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Thus, only table &lt;code&gt;t1&lt;/code&gt; is correctly identified as being used by both &lt;code&gt;t2&lt;/code&gt; and &lt;code&gt;t3&lt;/code&gt;, making it a suitable candidate for caching.&lt;/p&gt;&#xA;&lt;p&gt;While Catalyst is primarily used for logical optimizations, its initial analysis phase can also be useful in cases like this, particularly when migrating large volumes of SQL business logic to Spark SQL. In practice, it proved helpful in identifying a few tables that did, in fact, need to be cached.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Functional builder pattern</title>
      <link>https://donderom.com/posts/functional-builder-pattern/</link>
      <pubDate>Sat, 11 Mar 2017 15:54:04 +0200</pubDate>
      <guid>https://donderom.com/posts/functional-builder-pattern/</guid>
      <description>&lt;p&gt;The builder pattern is used for constructing objects as a series of steps.&lt;/p&gt;&#xA;&lt;p&gt;Let&amp;rsquo;s use a database query builder as an example.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;scala.util.&lt;/span&gt;{&lt;span style=&#34;font-weight:bold&#34;&gt;Failure&lt;/span&gt;, &lt;span style=&#34;font-weight:bold&#34;&gt;Success&lt;/span&gt;, &lt;span style=&#34;font-weight:bold&#34;&gt;Try&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Query&lt;/span&gt;(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  table&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;String&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  keyspace&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Option&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;String&lt;/span&gt;] &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;None&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  select&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;List&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;String&lt;/span&gt;] &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Nil&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  where&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;List&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;String&lt;/span&gt;] &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Nil&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  limit&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Option&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;Int&lt;/span&gt;] &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;None&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold&#34;&gt;def&lt;/span&gt; build&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Try&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;String&lt;/span&gt;] &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-weight:bold&#34;&gt;for&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      table &lt;span style=&#34;font-weight:bold&#34;&gt;&amp;lt;-&lt;/span&gt; renderTable&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      select &lt;span style=&#34;font-weight:bold&#34;&gt;&amp;lt;-&lt;/span&gt; renderSelect&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      where &lt;span style=&#34;font-weight:bold&#34;&gt;&amp;lt;-&lt;/span&gt; renderWhere&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      limit &lt;span style=&#34;font-weight:bold&#34;&gt;&amp;lt;-&lt;/span&gt; renderLimit&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    } &lt;span style=&#34;font-weight:bold&#34;&gt;yield&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;s&amp;#34;select &lt;/span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;$select&lt;/span&gt;&lt;span style=&#34;font-style:italic&#34;&gt; from &lt;/span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;$table&lt;/span&gt;&lt;span style=&#34;font-style:italic&#34;&gt; &lt;/span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;$where&lt;/span&gt;&lt;span style=&#34;font-style:italic&#34;&gt; &lt;/span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;$limit&lt;/span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;&lt;/span&gt;.trim&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold&#34;&gt;def&lt;/span&gt; keyspace(name&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;String&lt;/span&gt;)&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Query&lt;/span&gt; =&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-weight:bold&#34;&gt;this&lt;/span&gt;.copy(keyspace &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Option&lt;/span&gt;(name).filterNot(&lt;span style=&#34;font-weight:bold&#34;&gt;_&lt;/span&gt;.isEmpty))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold&#34;&gt;def&lt;/span&gt; select(col&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;String&lt;/span&gt;)&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Query&lt;/span&gt; =&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-weight:bold&#34;&gt;this&lt;/span&gt;.copy(select &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; col &lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;select&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold&#34;&gt;def&lt;/span&gt; where(clause&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;String&lt;/span&gt;)&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Query&lt;/span&gt; =&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-weight:bold&#34;&gt;this&lt;/span&gt;.copy(where &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; clause &lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;where&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold&#34;&gt;def&lt;/span&gt; limit(l&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Int&lt;/span&gt;)&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Query&lt;/span&gt; =&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-weight:bold&#34;&gt;this&lt;/span&gt;.copy(limit &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Option&lt;/span&gt;(l))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;def&lt;/span&gt; renderTable&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Try&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;String&lt;/span&gt;] &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-weight:bold&#34;&gt;if&lt;/span&gt; (table.isEmpty) &lt;span style=&#34;font-weight:bold&#34;&gt;Failure&lt;/span&gt;(error(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;Table name cannot be empty&amp;#34;&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-weight:bold&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Success&lt;/span&gt;(keyspace.filterNot(&lt;span style=&#34;font-weight:bold&#34;&gt;_&lt;/span&gt;.isEmpty).fold(table)(k &lt;span style=&#34;font-weight:bold&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;s&amp;#34;&lt;/span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;$k&lt;/span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;$table&lt;/span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;def&lt;/span&gt; renderSelect&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Try&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;String&lt;/span&gt;] &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-weight:bold&#34;&gt;Success&lt;/span&gt;(&lt;span style=&#34;font-weight:bold&#34;&gt;if&lt;/span&gt; (select.isEmpty) &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;*&amp;#34;&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;else&lt;/span&gt; select.mkString(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;, &amp;#34;&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;def&lt;/span&gt; renderWhere&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Try&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;String&lt;/span&gt;] &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-weight:bold&#34;&gt;Success&lt;/span&gt;(&lt;span style=&#34;font-weight:bold&#34;&gt;if&lt;/span&gt; (where.isEmpty) &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;s&amp;#34;&amp;#34;&amp;#34;where &lt;/span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;${&lt;/span&gt;where.mkString(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34; and &amp;#34;&lt;/span&gt;)&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;def&lt;/span&gt; renderLimit&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Try&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;String&lt;/span&gt;] &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    limit.fold(&lt;span style=&#34;font-weight:bold&#34;&gt;Try&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)) { l &lt;span style=&#34;font-weight:bold&#34;&gt;=&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;font-weight:bold&#34;&gt;if&lt;/span&gt; (l &amp;lt; 1) &lt;span style=&#34;font-weight:bold&#34;&gt;Failure&lt;/span&gt;(error(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;Limit must be strictly positive&amp;#34;&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;font-weight:bold&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Success&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;s&amp;#34;limit &lt;/span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;$l&lt;/span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;def&lt;/span&gt; error(msg&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;String&lt;/span&gt;)&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;IllegalArgumentException&lt;/span&gt; =&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-weight:bold&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;IllegalArgumentException&lt;/span&gt;(msg)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With some basic validation, there are strings everywhere for the sake of simplicity, as it&amp;rsquo;s not about type-safety now. The query can be constructed as follows:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;val&lt;/span&gt; query &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Query&lt;/span&gt;(table &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;test&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  .select(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;col&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  .where(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;col2 = 1&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  .limit(1)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  .keyspace(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;stg&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  .build&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;scala&amp;gt; scala.util.&lt;span style=&#34;font-weight:bold&#34;&gt;Try&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;String&lt;/span&gt;] &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Success&lt;/span&gt;(select col from stg.test where col2 &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; 1 limit 1)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;functional-approach&#34;&gt;Functional approach&lt;/h2&gt;&#xA;&lt;p&gt;&lt;span class=&#34;newthought&#34;&gt;The gist&lt;/span&gt;&#xA; of the functional approach is making each transformation step autonomous by disentangling it from the builder. In other words, each step becomes a first-class citizen function.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;object&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Query&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold&#34;&gt;def&lt;/span&gt; keyspace(name&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;String&lt;/span&gt;)&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Query&lt;/span&gt; =&amp;gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Query&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;_&lt;/span&gt;.keyspace(name)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold&#34;&gt;def&lt;/span&gt; select(col&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;String&lt;/span&gt;)&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt;    &lt;span style=&#34;&#34;&gt;Query&lt;/span&gt; =&amp;gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Query&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;_&lt;/span&gt;.select(col)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold&#34;&gt;def&lt;/span&gt; where(clause&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;String&lt;/span&gt;)&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt;  &lt;span style=&#34;&#34;&gt;Query&lt;/span&gt; =&amp;gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Query&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;_&lt;/span&gt;.where(clause)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold&#34;&gt;def&lt;/span&gt; limit(value&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Int&lt;/span&gt;)&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt;      &lt;span style=&#34;&#34;&gt;Query&lt;/span&gt; =&amp;gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Query&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;_&lt;/span&gt;.limit(value)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Each step is an explicit &lt;code&gt;Query =&amp;gt; Query&lt;/code&gt; function that delegates the construction logic to the builder. The benefits of such wrapping include:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;The original builder can be used without any changes.&lt;/li&gt;&#xA;&lt;li&gt;The construction can be composed in multiple steps (e.g. query optimization based on client request).&lt;/li&gt;&#xA;&lt;li&gt;Conditional steps become possible (try to add the &lt;code&gt;where&lt;/code&gt; step conditionally in the example above).&lt;/li&gt;&#xA;&lt;li&gt;Parts of the construction can be done elsewhere and passed for composition.&lt;/li&gt;&#xA;&lt;li&gt;New steps can be created without the original builder being aware of them.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;The initial example can be written like this:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Query._&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;val&lt;/span&gt; baseQuery &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Query&lt;/span&gt;(table &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;test&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;val&lt;/span&gt; steps &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; select(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;col&amp;#34;&lt;/span&gt;) andThen where(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;col2 = 1&amp;#34;&lt;/span&gt;) andThen limit(1) andThen keyspace(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;stg&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;steps(baseQuery).build&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;scala&amp;gt; scala.util.&lt;span style=&#34;font-weight:bold&#34;&gt;Try&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;String&lt;/span&gt;] &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Success&lt;/span&gt;(select col from stg.test where col2 &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; 1 limit 1)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;One of the potential drawbacks is chain ergonomics with all these &lt;code&gt;andThen&lt;/code&gt;s. It can be improved by adding a shorter alias to the &lt;code&gt;Function1&lt;/code&gt; type:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;implicit&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;FunctionAlias&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;A&lt;/span&gt;, &lt;span style=&#34;&#34;&gt;B&lt;/span&gt;](f&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Function1&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;A&lt;/span&gt;, &lt;span style=&#34;&#34;&gt;B&lt;/span&gt;]) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold&#34;&gt;def&lt;/span&gt; ~[&lt;span style=&#34;&#34;&gt;C&lt;/span&gt;](g&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;B&lt;/span&gt; =&amp;gt; C)&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;A&lt;/span&gt; =&amp;gt; C &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; f andThen g&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;val&lt;/span&gt; steps &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; select(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;col&amp;#34;&lt;/span&gt;) ~ where(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;col2 = 1&amp;#34;&lt;/span&gt;) ~ limit(1) ~ keyspace(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;stg&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;steps(baseQuery).build&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;scala&amp;gt; scala.util.&lt;span style=&#34;font-weight:bold&#34;&gt;Try&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;String&lt;/span&gt;] &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Success&lt;/span&gt;(select col from stg.test where col2 &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; 1 limit 1)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Another example of function composition is creating a step from a list of columns:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;val&lt;/span&gt; identity &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; (q&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Query&lt;/span&gt;) &lt;span style=&#34;font-weight:bold&#34;&gt;=&amp;gt;&lt;/span&gt; q&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;val&lt;/span&gt; cols &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;List&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;col1&amp;#34;&lt;/span&gt;, &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;col2&amp;#34;&lt;/span&gt;, &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;col3&amp;#34;&lt;/span&gt;).foldLeft(identity)(&lt;span style=&#34;font-weight:bold&#34;&gt;_&lt;/span&gt; ~ select(&lt;span style=&#34;font-weight:bold&#34;&gt;_&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;val&lt;/span&gt; steps &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; keyspace(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;namespace&amp;#34;&lt;/span&gt;) ~ cols&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;steps(baseQuery).build&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;scala&amp;gt; scala.util.&lt;span style=&#34;font-weight:bold&#34;&gt;Try&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;String&lt;/span&gt;] &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Success&lt;/span&gt;(select col3, col2, col1 from namespace.test)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;arrows&#34;&gt;Arrows&lt;/h2&gt;&#xA;&lt;p&gt;If you already use &lt;a href=&#34;https://typelevel.org/cats&#34;&gt;Cats&lt;/a&gt; or need additional functionality, the same can be accomplished with the &lt;a href=&#34;https://typelevel.org/cats/typeclasses/arrow.html&#34;&gt;Arrow&lt;/a&gt; type class.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;cats.implicits._&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;val&lt;/span&gt; identity &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; cats.arrow.&lt;span style=&#34;font-weight:bold&#34;&gt;Arrow&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;Function1&lt;/span&gt;].id[&lt;span style=&#34;&#34;&gt;Query&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;val&lt;/span&gt; cols &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;List&lt;/span&gt;(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;col1&amp;#34;&lt;/span&gt;, &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;col2&amp;#34;&lt;/span&gt;, &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;col3&amp;#34;&lt;/span&gt;).foldLeft(identity)(&lt;span style=&#34;font-weight:bold&#34;&gt;_&lt;/span&gt; &amp;gt;&amp;gt;&amp;gt; select(&lt;span style=&#34;font-weight:bold&#34;&gt;_&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;val&lt;/span&gt; steps &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; keyspace(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;namespace&amp;#34;&lt;/span&gt;) &amp;gt;&amp;gt;&amp;gt; limit(1) &amp;gt;&amp;gt;&amp;gt; select(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;col&amp;#34;&lt;/span&gt;) &amp;gt;&amp;gt;&amp;gt; cols&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;steps(baseQuery).build&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;scala&amp;gt; scala.util.&lt;span style=&#34;font-weight:bold&#34;&gt;Try&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;String&lt;/span&gt;] &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Success&lt;/span&gt;(select col3, col2, col1, col from namespace.test  limit 1)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note that &lt;code&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/code&gt; is identical to ours &lt;code&gt;~&lt;/code&gt; as it&amp;rsquo;s just an alias for &lt;code&gt;andThen&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;The whole construction can be built from any collection(s) of steps:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;List&lt;/span&gt;(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  keyspace(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;namespace&amp;#34;&lt;/span&gt;),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  limit(1),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  select(&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;col&amp;#34;&lt;/span&gt;),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  cols&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;).foldLeft(identity)(&lt;span style=&#34;font-weight:bold&#34;&gt;_&lt;/span&gt; &amp;gt;&amp;gt;&amp;gt; &lt;span style=&#34;font-weight:bold&#34;&gt;_&lt;/span&gt;)(baseQuery).build&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The unobtrusive functional layer on top of any builder is what makes it an elegant approach in my opinion.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Akka persistent views</title>
      <link>https://donderom.com/posts/akka-persistent-views/</link>
      <pubDate>Fri, 10 Apr 2015 22:09:55 +0300</pubDate>
      <guid>https://donderom.com/posts/akka-persistent-views/</guid>
      <description>&lt;p&gt;In this post, I&amp;rsquo;d like to talk about certain aspects of Akka persistence, which is still experimental at the time of writing. The official documentation does a good job of explaining the concept behind Akka persistence and how to make an actor persistent. However, I noticed one thing that can be confusing to many people when they start dealing with akka-persistence module&amp;mdash;Persistent Views. Before we discuss it, let&amp;rsquo;s start with the pattern that will help to understand persistent views better.&lt;/p&gt;&#xA;&lt;h2 id=&#34;cqrs-command-query-responsibility-segregation&#34;&gt;CQRS (Command Query Responsibility Segregation)&lt;/h2&gt;&#xA;&lt;p&gt;The idea of the CQRS pattern is to use separate models for reading and updating data, rather than the more classical way of performing CRUD (create, read, update, delete) operations at the same time and place. In terms of CRUD, create, update, and delete are treated as &lt;strong&gt;C&lt;/strong&gt;ommands, while read is considered to be a &lt;strong&gt;Q&lt;/strong&gt;uery. One of the architectural patterns that CRQS fits with is Event Sourcing (you can read about how Akka persistence implements this idea in the official documentation). Before we delve into persistent views, let&amp;rsquo;s first take a quick look at non-persistent actors.&lt;/p&gt;&#xA;&lt;h2 id=&#34;cqrs-with-non-persistent-actors&#34;&gt;CQRS with Non Persistent Actors&lt;/h2&gt;&#xA;&lt;p&gt;Let&amp;rsquo;s imagine that there is an actor that keeps transactions in its internal state. We can use the same actor to store new transactions and read them. One of the issues with such a design is the need for having completely different representations for display. For instance, we may want to display all invalid transactions and the current balance for a particular account on the administration panel. If the actor is in the middle of updating its state, it cannot be queried, which makes the system less responsive. To separate commands and queries, we will most likely end up with more than one actor, each with its own state reflecting changes from the main actor. This setup will work pretty well until the main actor, with all the transactions, goes down. Once this happens, the state is lost forever. The other actors can still function but the data will stay out of sync.&lt;/p&gt;&#xA;&lt;h2 id=&#34;persistent-views-and-cqrs&#34;&gt;Persistent Views and CQRS&lt;/h2&gt;&#xA;&lt;p&gt;To make it clear, Persistent View represents the Query part of CQRS. Persistent actors are responsible for managing state (persisting events, deleting messages, restoring from the journal, saving the snapshots, etc.). Persistent views poll messages directly from the journal of a persistent actor, without being coupled to the actor itself. All they have to know is the identifier of the corresponding persistent actor. If that persistent actor crashes, it does not affect persistent views as they can still serve data from the journal. Once the persistent actor is recovered, persistent views will eventually become consistent with it again. Moreover, a persistent view can utilize other data sources to optimize data presentation. Basically, it comes down to this: use persistent actors to handle commands and persistent views to handle queries.&lt;/p&gt;&#xA;&lt;h2 id=&#34;persistent-views-in-action&#34;&gt;Persistent Views in Action&lt;/h2&gt;&#xA;&lt;p&gt;The working demo project can be found &lt;a href=&#34;https://github.com/donderom/monadzoo-persistent-view&#34;&gt;here&lt;/a&gt;. The source code consists of three files: one for the persistent actor and the other two for the persistent views. The &lt;code&gt;TransactionActor&lt;/code&gt; is a persistent actor responsible for persisting transactions. All the machinery in the source code is primarily focused on maintaining and recovering state. The &lt;code&gt;InvalidTransactionsView&lt;/code&gt; is a persistent view that shows all invalid transactions. You can see how it&amp;rsquo;s decoupled from the &lt;code&gt;TransactionActor&lt;/code&gt;. Another persistent view is the &lt;code&gt;BalanceView&lt;/code&gt;. Instead of keeping a list of transactions, it keeps a map where it stores the current amount per account. In a traditional approach, both persistent views would be methods of the same object. With separate views, even if the persistent actor and one of the views die, the remaining view will still be able to process queries.&lt;/p&gt;&#xA;&lt;p&gt;I think Persistent Views play an important role when being used with event sourcing. They help to follow the CQRS pattern more easily than using regular actors would. I would even argue that they are just as important as persistent actors. The official documentation would be more helpful if it provided more information about them.&#xA;I hope that the analogy with CQRS can help develop a better understanding of their role.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Scala Function object to the rescue</title>
      <link>https://donderom.com/posts/scala-function-object-to-the-rescue/</link>
      <pubDate>Wed, 01 Apr 2015 20:43:55 +0300</pubDate>
      <guid>https://donderom.com/posts/scala-function-object-to-the-rescue/</guid>
      <description>&lt;p&gt;The &lt;code&gt;Function&lt;/code&gt; object has been there since Scala version 1.0. It provides a bunch of utility methods for dealing with higher-order functions. Despite its simplicity and usefulness, I have found that not only do many novice developers not use them, but they are also unaware of their existence. In this post, I would like to bring to your attention some of the functions I find helpful in certain scenarios.&lt;/p&gt;&#xA;&lt;h3 id=&#34;chain&#34;&gt;&lt;code&gt;chain&lt;/code&gt;&lt;/h3&gt;&#xA;&lt;p&gt;To get started, let&amp;rsquo;s introduce two extremely simple functions from &lt;code&gt;Int&lt;/code&gt; to &lt;code&gt;Int&lt;/code&gt;. One of them, let&amp;rsquo;s call it &lt;code&gt;inc&lt;/code&gt;, will increase a number by one, while the other one, &lt;code&gt;double&lt;/code&gt;, with multiply a number by two.&lt;em&gt;&lt;small&gt; (We could use a shorter definition form such as &lt;code&gt;val inc: Int =&amp;gt; Int = _ + 1&lt;/code&gt; for both functions.)&lt;/small&gt;&lt;/em&gt;&#xA;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;scala&amp;gt; &lt;span style=&#34;font-weight:bold&#34;&gt;val&lt;/span&gt; inc&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Int&lt;/span&gt; =&amp;gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Int&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; x &lt;span style=&#34;font-weight:bold&#34;&gt;=&amp;gt;&lt;/span&gt; x + 1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;scala&amp;gt; &lt;span style=&#34;font-weight:bold&#34;&gt;val&lt;/span&gt; double&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Int&lt;/span&gt; =&amp;gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Int&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; x &lt;span style=&#34;font-weight:bold&#34;&gt;=&amp;gt;&lt;/span&gt; x * 2&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So what do we do when we have a sequence of such functions that we want to combine? One of the most popular options in my practice is the following:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;scala&amp;gt; &lt;span style=&#34;font-weight:bold&#34;&gt;List&lt;/span&gt;(inc, double, inc) reduce (&lt;span style=&#34;font-weight:bold&#34;&gt;_&lt;/span&gt; andThen &lt;span style=&#34;font-weight:bold&#34;&gt;_&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This piece of code takes a sequence of functions and combines them using the &lt;code&gt;andThen&lt;/code&gt; method starting with the first one in the list and returns the resulting function from &lt;code&gt;Int&lt;/code&gt; to &lt;code&gt;Int&lt;/code&gt;. It is by no means a bad code. Let&amp;rsquo;s see how we can simplify it by using the &lt;code&gt;chain&lt;/code&gt; function from the &lt;code&gt;Function&lt;/code&gt; object:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;scala&amp;gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Function&lt;/span&gt;.chain(&lt;span style=&#34;font-weight:bold&#34;&gt;List&lt;/span&gt;(inc, double, inc))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It does exactly the same as the previous snippet but it might be more intuitive. If we first import all the functions from the &lt;code&gt;Function&lt;/code&gt; object, it will be dead simple:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;scala&amp;gt; &lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Function._&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;scala&amp;gt; chain(&lt;span style=&#34;font-weight:bold&#34;&gt;List&lt;/span&gt;(inc, double, inc))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you want to use the &lt;code&gt;compose&lt;/code&gt; function instead of the &lt;code&gt;andThen&lt;/code&gt;, you can still achieve the same with the &lt;code&gt;chain&lt;/code&gt; by reversing the sequence first:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;scala&amp;gt; chain(&lt;span style=&#34;font-weight:bold&#34;&gt;List&lt;/span&gt;(inc, double, double).reverse)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;tupled&#34;&gt;&lt;code&gt;tupled&lt;/code&gt;&lt;/h3&gt;&#xA;&lt;p&gt;Imagine there is a tuple of two elements, id and name, representing a user wrapped into &lt;code&gt;Option&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;scala&amp;gt; &lt;span style=&#34;font-weight:bold&#34;&gt;val&lt;/span&gt; user&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Option&lt;/span&gt;[(&lt;span style=&#34;&#34;&gt;Int&lt;/span&gt;, &lt;span style=&#34;&#34;&gt;String&lt;/span&gt;)] &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Some&lt;/span&gt;(1, &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;Bob&amp;#34;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And a function, let&amp;rsquo;s call it &lt;code&gt;auth&lt;/code&gt;, which accepts id and name as &lt;strong&gt;two separate arguments&lt;/strong&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;scala&amp;gt; &lt;span style=&#34;font-weight:bold&#34;&gt;val&lt;/span&gt; auth&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; (&lt;span style=&#34;&#34;&gt;Int&lt;/span&gt;, &lt;span style=&#34;&#34;&gt;String&lt;/span&gt;) &lt;span style=&#34;font-weight:bold&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Boolean&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; (id, name) &lt;span style=&#34;font-weight:bold&#34;&gt;=&amp;gt;&lt;/span&gt; id == 1 &amp;amp;&amp;amp; name == &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;Bob&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can&amp;rsquo;t just map the &lt;code&gt;auth&lt;/code&gt; function over the &lt;code&gt;user&lt;/code&gt; because the former expects two arguments while our user is a tuple with two elements. One option would be to extract the id and name and pass them as separate arguments to the &lt;code&gt;auth&lt;/code&gt; function but it requires some boilerplate to write. That&amp;rsquo;s when we can use the &lt;code&gt;tupled&lt;/code&gt; function from the &lt;code&gt;Function&lt;/code&gt; object. It takes a function with, let&amp;rsquo;s say, two arguments, and converts it into a function that takes a tuple of two elements with the same types as the original arguments. That&amp;rsquo;s exactly what we need to map the &lt;code&gt;auth&lt;/code&gt; function over the user:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;scala&amp;gt; user map auth.tupled&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;res4&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Option&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;Boolean&lt;/span&gt;] &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Some&lt;/span&gt;(&lt;span style=&#34;font-weight:bold&#34;&gt;true&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There are &lt;code&gt;tupled&lt;/code&gt; functions defined for functions with arity from two to five inclusive. I find it convenient as I don&amp;rsquo;t use tuples of more than two or three elements that often.&lt;/p&gt;&#xA;&lt;p&gt;Technically, in the previous example, the &lt;code&gt;tupled&lt;/code&gt; was called on the function itself (as it&amp;rsquo;s defined both in the &lt;code&gt;Function&lt;/code&gt; object and the &lt;code&gt;Function*&lt;/code&gt; traits). Another &amp;ldquo;trick&amp;rdquo; that can be useful is mapping over a hash map in what many people would say a natural way. (It also demonstrates the usage of the &lt;code&gt;tupled&lt;/code&gt; function from the &lt;code&gt;Function&lt;/code&gt; object and not defined in the &lt;code&gt;Function*&lt;/code&gt; trait.) In Scala, you can&amp;rsquo;t write code like this:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;scala&amp;gt; &lt;span style=&#34;font-weight:bold&#34;&gt;val&lt;/span&gt; m &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Map&lt;/span&gt;(1 -&amp;gt; &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;first&amp;#34;&lt;/span&gt;, 2 -&amp;gt; &lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;second&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;scala&amp;gt; m map { (k, v) &lt;span style=&#34;font-weight:bold&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;s&amp;#34;&lt;/span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;$k&lt;/span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;$v&lt;/span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;&lt;/span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;console&amp;gt;:12: error: missing parameter type&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Note: The expected type requires a one-argument function accepting a 2-Tuple.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      Consider a pattern matching anonymous function, `{ case (k, v) =&amp;gt;  ... }`&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              m map { (k, v) =&amp;gt; s&amp;#34;$k:$v&amp;#34; }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                       ^&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;console&amp;gt;:12: error: missing parameter type&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              m map { (k, v) =&amp;gt; s&amp;#34;$k:$v&amp;#34; }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;What many developers would do is something like this:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;scala&amp;gt; m map { &lt;span style=&#34;font-weight:bold&#34;&gt;case&lt;/span&gt; (k, v) &lt;span style=&#34;font-weight:bold&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;s&amp;#34;&lt;/span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;$k&lt;/span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;$v&lt;/span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;&lt;/span&gt; }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;res5&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;scala.collection.immutable.Iterable&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;String&lt;/span&gt;] &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;List&lt;/span&gt;(1&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;&#34;&gt;first&lt;/span&gt;, 2&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;&#34;&gt;second&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can achieve the same using the &lt;code&gt;tupled&lt;/code&gt; function:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;scala&amp;gt; m map tupled { (k, v) &lt;span style=&#34;font-weight:bold&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span style=&#34;font-style:italic&#34;&gt;s&amp;#34;&lt;/span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;$k&lt;/span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;$v&lt;/span&gt;&lt;span style=&#34;font-style:italic&#34;&gt;&amp;#34;&lt;/span&gt; }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;res6&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;scala.collection.immutable.Iterable&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;String&lt;/span&gt;] &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;List&lt;/span&gt;(1&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;&#34;&gt;first&lt;/span&gt;, 2&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;&#34;&gt;second&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Maybe not so useful but it helps to understand its application.&lt;/p&gt;&#xA;&lt;h3 id=&#34;unlift&#34;&gt;&lt;code&gt;unlift&lt;/code&gt;&lt;/h3&gt;&#xA;&lt;p&gt;To illustrate the usage of the &lt;code&gt;unlift&lt;/code&gt;, let&amp;rsquo;s write a function that takes an &lt;code&gt;Int&lt;/code&gt; and returns &lt;code&gt;Some(x)&lt;/code&gt; if &lt;code&gt;x&lt;/code&gt; is equal to or greater than zero, and &lt;code&gt;None&lt;/code&gt; otherwise:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;scala&amp;gt; &lt;span style=&#34;font-weight:bold&#34;&gt;val&lt;/span&gt; f&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Int&lt;/span&gt; =&amp;gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Option&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;Int&lt;/span&gt;] &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Option&lt;/span&gt;(&lt;span style=&#34;font-weight:bold&#34;&gt;_&lt;/span&gt;) filter (&lt;span style=&#34;font-weight:bold&#34;&gt;_&lt;/span&gt; &amp;gt;= 0)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;unlift&lt;/code&gt; function turns an &lt;code&gt;A =&amp;gt; Option[B]&lt;/code&gt; function into a &lt;code&gt;PartialFunction[A, B]&lt;/code&gt;. It allows us to use our function in any place where a partial function is required. To make it clear, this how we can use our function to filter out negative integers from a list:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;scala&amp;gt; &lt;span style=&#34;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;Function._&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;scala&amp;gt; &lt;span style=&#34;font-weight:bold&#34;&gt;List&lt;/span&gt;(-1,0,1) collect unlift(f)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;res7&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;List&lt;/span&gt;[&lt;span style=&#34;&#34;&gt;Int&lt;/span&gt;] &lt;span style=&#34;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;List&lt;/span&gt;(0, 1)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I don&amp;rsquo;t use this on a daily basis but there are cases like this where it comes very handy.&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;Bonus&lt;/em&gt;: there is an opposite function defined in the &lt;code&gt;PartialFunction&lt;/code&gt; called &lt;code&gt;lift&lt;/code&gt;. To see how it is related to the &lt;code&gt;unlift&lt;/code&gt; function, the following equation is always true:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scala&#34; data-lang=&#34;scala&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;scala&amp;gt; f == unlift(f).lift&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;res8&lt;span style=&#34;font-weight:bold&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;&#34;&gt;Boolean&lt;/span&gt; = &lt;span style=&#34;font-weight:bold&#34;&gt;true&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;uncurrieduntupled&#34;&gt;&lt;code&gt;uncurried/untupled&lt;/code&gt;&lt;/h3&gt;&#xA;&lt;p&gt;These two functions are the opposite to the &lt;code&gt;curried&lt;/code&gt; and the &lt;code&gt;tupled&lt;/code&gt; functions, respectively. I haven&amp;rsquo;t seen them being used as frequently as the ones described above.&lt;/p&gt;&#xA;&lt;p&gt;Although there is nothing new, I believe that we often overlook some of the useful API provided by the Scala standard library. I hope that this refresher is on time and can save you a few lines of code every now and then.&lt;/p&gt;&#xA;</description>
    </item>
  </channel>
</rss>
