<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Functional on Ocarina — manual</title>
    <link>https://mojo-molotov.github.io/from-ocarina-to-igor/series/functional/</link>
    <description>Recent content in Functional on Ocarina — manual</description>
    <generator>Hugo</generator>
    <language>en-US</language>
    <lastBuildDate>Thu, 28 May 2026 21:02:53 +0200</lastBuildDate>
    <atom:link href="https://mojo-molotov.github.io/from-ocarina-to-igor/series/functional/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>03.01 — Effect, Thunk[T], Result[T]</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/03-functional/01-effect-thunk-result/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/03-functional/01-effect-thunk-result/</guid>
      <description>&lt;h1 id=&#34;0301effect-thunkt-resultt&#34;&gt;03.01 — &lt;code&gt;Effect&lt;/code&gt;, &lt;code&gt;Thunk[T]&lt;/code&gt;, &lt;code&gt;Result[T]&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#0301effect-thunkt-resultt&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;Three lines in &lt;code&gt;custom_types/&lt;/code&gt; + one in &lt;code&gt;railway/&lt;/code&gt;. The whole DSL rides on them.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;triplet&#34;&gt;Triplet&lt;a class=&#34;anchor&#34; href=&#34;#triplet&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# src/ocarina/custom_types/effect.py&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;color:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; Effect &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Callable[[], &lt;span style=&#34;color:#ff79c6&#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;color:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; Effects &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;tuple&lt;/span&gt;[Effect, &lt;span style=&#34;color:#ff79c6&#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;color:#6272a4&#34;&gt;# src/ocarina/custom_types/thunk.py&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;color:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; Thunk[T] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Callable[[], T]&#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;color:#6272a4&#34;&gt;# src/ocarina/railway/result.py&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;color:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; Result[T] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Ok[T] &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; Fail&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Type&lt;/th&gt;&#xA;          &lt;th&gt;FP semantics&lt;/th&gt;&#xA;          &lt;th&gt;Definition&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;Effect&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Side effect (deferred)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;() -&amp;gt; None&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;Thunk[T]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Deferred &lt;em&gt;computation&lt;/em&gt; with value&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;() -&amp;gt; T&lt;/code&gt; (laziness + value)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;Result[T]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;em&gt;Computation&lt;/em&gt; that can fail&lt;/td&gt;&#xA;          &lt;td&gt;Discriminated union &lt;code&gt;Ok[T] | Fail&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;effect-vs-thunk-distinction&#34;&gt;Effect vs Thunk distinction&lt;a class=&#34;anchor&#34; href=&#34;#effect-vs-thunk-distinction&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;log_msg: Effect &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;lambda&lt;/span&gt;: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;hello&amp;#34;&lt;/span&gt;)        &lt;span style=&#34;color:#6272a4&#34;&gt;# () -&amp;gt; None&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;get_42: Thunk[&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;int&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;lambda&lt;/span&gt;: &lt;span style=&#34;color:#bd93f9&#34;&gt;42&lt;/span&gt;                 &lt;span style=&#34;color:#6272a4&#34;&gt;# () -&amp;gt; int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fetch:  Thunk[Result[&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;int&lt;/span&gt;]] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;lambda&lt;/span&gt;: Ok(&lt;span style=&#34;color:#bd93f9&#34;&gt;42&lt;/span&gt;)     &lt;span style=&#34;color:#6272a4&#34;&gt;# () -&amp;gt; Result[int]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Function &lt;em&gt;returns&lt;/em&gt; something consumed elsewhere → &lt;code&gt;Thunk[T]&lt;/code&gt;.&lt;br&gt;&#xA;Otherwise → &lt;code&gt;Effect&lt;/code&gt;.&lt;/p&gt;</description>
    </item>
    <item>
      <title>03.02 — Closures as the primitive of inversion of control</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/03-functional/02-closures-ioc/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/03-functional/02-closures-ioc/</guid>
      <description>&lt;h1 id=&#34;0302closures-as-the-primitive-of-inversion-of-control&#34;&gt;03.02 — Closures as the primitive of inversion of control&lt;a class=&#34;anchor&#34; href=&#34;#0302closures-as-the-primitive-of-inversion-of-control&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;No DI container. No injection framework. &lt;strong&gt;A closure is Ocarina&amp;rsquo;s injection primitive.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;the-idea&#34;&gt;The idea&lt;a class=&#34;anchor&#34; href=&#34;#the-idea&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;A closure captures values from its enclosing scope. Return a function from another function and the outer parameters are &lt;strong&gt;available inside&lt;/strong&gt;, even after the outer function returns.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;make_handler&lt;/span&gt;(logger: ILogger) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; Callable[[&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;], FailureHandler]:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;make_failure_handler&lt;/span&gt;(msg: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; FailureHandler:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;actual_handler&lt;/span&gt;(exc: Exception) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            logger&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;error(msg, exc&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;exc)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; actual_handler&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; make_failure_handler&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Also &lt;em&gt;currying&lt;/em&gt;: &lt;code&gt;make_handler(logger)(msg)(exc)&lt;/code&gt;. Each call captures a new layer of environment.&lt;/p&gt;</description>
    </item>
    <item>
      <title>03.03 — Lazy evaluation</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/03-functional/03-lazy-evaluation/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/03-functional/03-lazy-evaluation/</guid>
      <description>&lt;h1 id=&#34;0303lazy-evaluation&#34;&gt;03.03 — Lazy evaluation&lt;a class=&#34;anchor&#34; href=&#34;#0303lazy-evaluation&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;In Ocarina, &lt;strong&gt;nothing is executed until you explicitly trigger it&lt;/strong&gt;. That&amp;rsquo;s what makes scenarios composable as values.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;zzz&#34;&gt;Zzz&lt;a class=&#34;anchor&#34; href=&#34;#zzz&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Location&lt;/th&gt;&#xA;          &lt;th&gt;Form&lt;/th&gt;&#xA;          &lt;th&gt;Trigger&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;ChainRunner[T]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Thunk[ActionChain[T]]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;runner.run()&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;validate(...)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;ValidationStartBlock&lt;/code&gt; / &lt;code&gt;ValidationAssertBlock&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;.execute()&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;match_page(...)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;returns a &lt;code&gt;ChainRunner[Any]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;.run()&lt;/code&gt; (via the surrounding chain)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;Watcher.callback&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Callable[[Watcher], None]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;_loop&lt;/code&gt; when &lt;code&gt;start()&lt;/code&gt; is called&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;logger.set_prefix(thunk)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Thunk[str]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;recomputed at each log call&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;Scenario.setup&lt;/code&gt; / &lt;code&gt;teardown&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Effect&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;called by &lt;code&gt;TestExecutor&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;bootstrap(post_exec=...)&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Callable[[TestCycleResults], None]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;called after &lt;code&gt;run_plugins&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;CliBuilder(effects_factory=lambda ns: (...))&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Effects&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;called after the argparse parse&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;test_scenario: TestScenario[Driver]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;Callable[[Driver, ILogger], Scenario[Driver]]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;called by &lt;code&gt;Test.spawn&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;dispatch[mode]()&lt;/code&gt; in &lt;code&gt;TestCycle.run_all&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;dict of &lt;code&gt;Thunk[bool]&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;called on lookup&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;chainrunner&#34;&gt;&lt;code&gt;ChainRunner&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#chainrunner&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;runner &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; drive_page(act1, act2, act3)        &lt;span style=&#34;color:#6272a4&#34;&gt;# ⚠️  nothing executed&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;color:#6272a4&#34;&gt;# … later …&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;chain &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; runner&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;run()                         &lt;span style=&#34;color:#6272a4&#34;&gt;# ▶︎  execution&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Capability&lt;/th&gt;&#xA;          &lt;th&gt;Without laziness&lt;/th&gt;&#xA;          &lt;th&gt;With laziness&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Store a scenario in a variable&lt;/td&gt;&#xA;          &lt;td&gt;impossible (already executed)&lt;/td&gt;&#xA;          &lt;td&gt;trivial&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Multiply &lt;code&gt;[runner] * 5&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;executes once, you get 5 refs to the result&lt;/td&gt;&#xA;          &lt;td&gt;executes 5 times&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Pass a &lt;code&gt;runner&lt;/code&gt; to another &lt;code&gt;runner&lt;/code&gt; (composition)&lt;/td&gt;&#xA;          &lt;td&gt;impossible&lt;/td&gt;&#xA;          &lt;td&gt;trivial&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Reorder &lt;code&gt;act&lt;/code&gt;s in a test refactor&lt;/td&gt;&#xA;          &lt;td&gt;hard&lt;/td&gt;&#xA;          &lt;td&gt;trivial&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;validateexecute&#34;&gt;&lt;code&gt;validate(...).execute()&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#validateexecute&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;v &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; validate(value, name&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;x&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;assert_that(is_positive)&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;assert_that(is_not_zero)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# … you can compose …&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;combined &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; chain_validations(v, other_validation)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# … nothing executed so far …&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;combined&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;execute()&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;raise_if_invalid()        &lt;span style=&#34;color:#6272a4&#34;&gt;# ▶︎  execution + aggregation&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s how &lt;code&gt;_ValidationChain&lt;/code&gt; &lt;strong&gt;collects&lt;/strong&gt; every error before raising a single one (&lt;code&gt;AggregateInvariantViolationError&lt;/code&gt;).&lt;/p&gt;</description>
    </item>
    <item>
      <title>03.04 — reduce / fold in Ocarina</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/03-functional/04-fold-reduce/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/03-functional/04-fold-reduce/</guid>
      <description>&lt;h1 id=&#34;0304reduce--fold-in-ocarina&#34;&gt;03.04 — &lt;code&gt;reduce&lt;/code&gt; / fold in Ocarina&lt;a class=&#34;anchor&#34; href=&#34;#0304reduce--fold-in-ocarina&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;One &lt;code&gt;reduce&lt;/code&gt; in the whole code base. But it&amp;rsquo;s &lt;strong&gt;the&lt;/strong&gt; central reducer — it&amp;rsquo;s what makes &lt;code&gt;chain_actions&lt;/code&gt; work.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;code&#34;&gt;Code&lt;a class=&#34;anchor&#34; href=&#34;#code&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# src/ocarina/dsl/testing_with_railway/chain_actions.py&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;color:#ff79c6&#34;&gt;from&lt;/span&gt; functools &lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; reduce&#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;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;chain_actions&lt;/span&gt;[T](&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    first: ActionSuccess[T], &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt;rest: ActionSuccess[T]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; ChainRunner[T]:&#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;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;thunk&lt;/span&gt;() &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; ActionChain[T]:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;reducer&lt;/span&gt;(chain: ActionChain[T], step: ActionSuccess[T]) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; ActionChain[T]:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; chain&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;has_failed():&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; chain&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                chain&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;then(step&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;__action__)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;failure(step&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;__failure_handler__)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;success(step&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;__success_handler__)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;execute()&#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;color:#ff79c6&#34;&gt;return&lt;/span&gt; reduce(reducer, rest, first&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;execute())&#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;color:#ff79c6&#34;&gt;return&lt;/span&gt; ChainRunner(thunk&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;thunk)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;anatomy&#34;&gt;Anatomy&lt;a class=&#34;anchor&#34; href=&#34;#anatomy&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;FP concept&lt;/th&gt;&#xA;          &lt;th&gt;Python realization&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Accumulator type&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;ActionChain[T]&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Element type&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;ActionSuccess[T]&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Reducer&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;reducer(chain, step) -&amp;gt; ActionChain[T]&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Initial value&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;first.execute()&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Iterable&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;rest&lt;/code&gt; (the &lt;code&gt;*rest&lt;/code&gt; tuple)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Short-circuit&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;if chain.has_failed(): return chain&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Result&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;ActionChain[T]&lt;/code&gt; (the last one)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;execution-flow&#34;&gt;Execution flow&lt;a class=&#34;anchor&#34; href=&#34;#execution-flow&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;initial = first.execute()                                          # → ActionChain(ok=True, result=Ok(...))&#xA;&#xA;step 1 : reducer(&amp;lt;initial&amp;gt;, act2) :&#xA;  chain.has_failed() = False&#xA;  chain.then(act2.__action__).failure(...).success(...).execute()&#xA;  → action raises → Fail&#xA;  → failure_handler(exc)  ❌ FIRE&#xA;  → ActionChain(ok=False, result=Fail(exc))&#xA;&#xA;step 2 : reducer(&amp;lt;failed&amp;gt;, act3) :&#xA;  chain.has_failed() = True&#xA;  return chain                                                     ⚠️  short-circuit&#xA;&#xA;result of the reduce = &amp;lt;failed ActionChain&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;why-reduce&#34;&gt;Why &lt;code&gt;reduce&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#why-reduce&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# Imperative approach (equivalent)&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;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;thunk&lt;/span&gt;() &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; ActionChain[T]:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    chain &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; first&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;execute()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt; step &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; rest:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; chain&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;has_failed():&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff79c6&#34;&gt;break&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        chain &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            chain&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;then(step&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;__action__)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;failure(step&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;__failure_handler__)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;success(step&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;__success_handler__)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;execute()&#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;color:#ff79c6&#34;&gt;return&lt;/span&gt; chain&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Reduce&lt;/th&gt;&#xA;          &lt;th&gt;Imperative loop&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;A single &lt;strong&gt;value&lt;/strong&gt; flows (the &lt;code&gt;chain&lt;/code&gt;)&lt;/td&gt;&#xA;          &lt;td&gt;A state + a mutation&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Reducer = pure function (testable in isolation)&lt;/td&gt;&#xA;          &lt;td&gt;Inline logic&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Explicit &amp;ldquo;&lt;em&gt;accumulator&lt;/em&gt;&amp;rdquo; semantics&lt;/td&gt;&#xA;          &lt;td&gt;Implicit logic&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Functional standard&lt;/td&gt;&#xA;          &lt;td&gt;Imperative standard&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;An &lt;strong&gt;expression of intent&lt;/strong&gt;: we &lt;em&gt;fold&lt;/em&gt; a list onto an accumulator. Semantics jump out on the first read. This is what&amp;rsquo;s called a &lt;strong&gt;catamorphism&lt;/strong&gt;.&lt;/p&gt;</description>
    </item>
    <item>
      <title>03.05 — Declarative programming</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/03-functional/05-declarative/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/03-functional/05-declarative/</guid>
      <description>&lt;h1 id=&#34;0305declarative-programming&#34;&gt;03.05 — Declarative programming&lt;a class=&#34;anchor&#34; href=&#34;#0305declarative-programming&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;An Ocarina scenario &lt;strong&gt;describes&lt;/strong&gt;, it doesn&amp;rsquo;t execute. That&amp;rsquo;s what makes it factorable, multipliable, readable.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;demonstration&#34;&gt;Demonstration&lt;a class=&#34;anchor&#34; href=&#34;#demonstration&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    drive_page(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        act(on_homepage, open_then_verify_homepage)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;failure(just_log_error(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Failed to reach the homepage...&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;color:#ff79c6&#34;&gt;.&lt;/span&gt;success(log_success_with_current_url_and_take_screenshot(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;On the homepage!&amp;#34;&lt;/span&gt;)),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        act(on_homepage, click_book_call_page_cta)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;failure(just_log_error(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Failed to click on the &amp;#39;Book a call&amp;#39; CTA...&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;color:#ff79c6&#34;&gt;.&lt;/span&gt;success(just_log_success(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Clicked on the &amp;#39;Book a call&amp;#39; CTA!&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;    drive_page(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        act(on_book_a_call_page, verify_book_call_page)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;failure(just_log_error(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Failed to verify the &amp;#39;Book a call&amp;#39; page...&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;color:#ff79c6&#34;&gt;.&lt;/span&gt;success(log_success_with_current_url_and_take_screenshot(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;On the &amp;#39;Book a call&amp;#39; page!&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;This code &lt;strong&gt;executes nothing&lt;/strong&gt;. It &lt;strong&gt;describes&lt;/strong&gt;:&lt;/p&gt;</description>
    </item>
    <item>
      <title>03.06 — PEP 695 generics in Ocarina</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/03-functional/06-pep-695-generics/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/03-functional/06-pep-695-generics/</guid>
      <description>&lt;h1 id=&#34;0306pep-695-generics-in-ocarina&#34;&gt;03.06 — PEP 695 generics in Ocarina&lt;a class=&#34;anchor&#34; href=&#34;#0306pep-695-generics-in-ocarina&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;Ocarina leans hard on the generics syntax from PEP 695 (Python 3.12+). It&amp;rsquo;s what keeps the whole typed ecosystem &lt;em&gt;from being a mess&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;three-forms-of-pep-695&#34;&gt;Three forms of PEP 695&lt;a class=&#34;anchor&#34; href=&#34;#three-forms-of-pep-695&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;1-generic-classes&#34;&gt;1. Generic classes&lt;a class=&#34;anchor&#34; href=&#34;#1-generic-classes&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@final&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;Ok&lt;/span&gt;[T](_BaseResult):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    value: T&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    error: &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#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;color:#ff79c6&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;TestSuite&lt;/span&gt;[Driver]:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#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;color:#ff79c6&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;Watcher&lt;/span&gt;[Driver]:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#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;color:#ff79c6&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;ChainRunner&lt;/span&gt;[T]:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#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;color:#ff79c6&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;ValidationStartBlock&lt;/span&gt;[T]:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#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;color:#ff79c6&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;CliStore&lt;/span&gt;[TKeys: &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&lt;/span&gt;]:  &lt;span style=&#34;color:#6272a4&#34;&gt;# with bound&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;color:#ff79c6&#34;&gt;...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Before PEP 695:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;from&lt;/span&gt; typing &lt;span style=&#34;color:#ff79c6&#34;&gt;import&lt;/span&gt; Generic, TypeVar&#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;T &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; TypeVar(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;T&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;color:#ff79c6&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;Ok&lt;/span&gt;(_BaseResult, Generic[T]):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    value: T&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    error: &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;2-parameterized-type-aliases&#34;&gt;2. Parameterized type aliases&lt;a class=&#34;anchor&#34; href=&#34;#2-parameterized-type-aliases&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; Result[T] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Ok[T] &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; Fail&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; Thunk[T] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Callable[[], T]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; Action[T] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Thunk[Result[T]]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; FailureHandler &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Callable[[Exception], &lt;span style=&#34;color:#ff79c6&#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;color:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; Predicate[T] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Callable[[T], &lt;span style=&#34;color:#ff79c6&#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;color:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; Effect &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Callable[[], &lt;span style=&#34;color:#ff79c6&#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;color:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; TestScenario[Driver] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Callable[[Driver, ILogger], Scenario[Driver]]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; TestScenarioFragment[Driver] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Callable[[Driver, ILogger], TestChain]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; TestChain &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Sequence[ChainRunner[Any]]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; TestWatchers[Driver] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Sequence[Watcher[Driver]] &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Before PEP 695:&lt;/p&gt;</description>
    </item>
    <item>
      <title>03.07 — Discriminated unions &#43; TypeGuard &#43; @final = sealed unions</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/03-functional/07-discriminated-unions-typeguards/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/03-functional/07-discriminated-unions-typeguards/</guid>
      <description>&lt;h1 id=&#34;0307discriminated-unions--typeguard--final--sealed-unions&#34;&gt;03.07 — Discriminated unions + TypeGuard + @final = &amp;ldquo;&lt;em&gt;sealed&lt;/em&gt;&amp;rdquo; unions&lt;a class=&#34;anchor&#34; href=&#34;#0307discriminated-unions--typeguard--final--sealed-unions&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;The combo that makes &lt;code&gt;Result[T]&lt;/code&gt; and &lt;code&gt;TestResult&lt;/code&gt; both &lt;strong&gt;typed&lt;/strong&gt; and &lt;strong&gt;usable without casts&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;discriminated-union&#34;&gt;Discriminated union&lt;a class=&#34;anchor&#34; href=&#34;#discriminated-union&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@final&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@dataclass(frozen&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;True&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;color:#ff79c6&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;Ok&lt;/span&gt;[T](_BaseResult):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    value: T&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    error: &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#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;@final&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@dataclass(frozen&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;True&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;color:#ff79c6&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;Fail&lt;/span&gt;(_BaseResult):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    error: Exception &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; field(default_factory&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;lambda&lt;/span&gt;: Exception(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Unknown error&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;color:#8be9fd;font-style:italic&#34;&gt;type&lt;/span&gt; Result[T] &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; Ok[T] &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; Fail&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;Two constructors: &lt;code&gt;Ok[T]&lt;/code&gt; and &lt;code&gt;Fail&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;Discriminant&lt;/em&gt; is the &lt;em&gt;isinstance check&lt;/em&gt; (not a field like &lt;code&gt;tag: Literal[&amp;quot;ok&amp;quot;, &amp;quot;fail&amp;quot;]&lt;/code&gt;).&lt;/li&gt;&#xA;&lt;li&gt;Both are &lt;code&gt;@final&lt;/code&gt; — &lt;em&gt;no&lt;/em&gt; possible subtype. The union is &lt;strong&gt;exhaustive&lt;/strong&gt;, i.e. &amp;ldquo;&lt;em&gt;sealed&lt;/em&gt;&amp;rdquo;.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;narrowing-typeguard&#34;&gt;Narrowing, &lt;code&gt;TypeGuard&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#narrowing-typeguard&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;is_ok&lt;/span&gt;[T](result: Result[T]) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; TypeGuard[Ok[T]]:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;isinstance&lt;/span&gt;(result, Ok)&#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;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;is_fail&lt;/span&gt;[T](result: Result[T]) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; TypeGuard[Fail]:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;isinstance&lt;/span&gt;(result, Fail)&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;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;handle_result&lt;/span&gt;(result: Result[&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;int&lt;/span&gt;]) &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;str&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;color:#ff79c6&#34;&gt;if&lt;/span&gt; is_ok(result):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Got: &lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;result&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;value&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;          &lt;span style=&#34;color:#6272a4&#34;&gt;# mypy: result is Ok[int], so .value exists&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;color:#ff79c6&#34;&gt;if&lt;/span&gt; is_fail(result):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Error: &lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;{&lt;/span&gt;result&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;error&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;        &lt;span style=&#34;color:#6272a4&#34;&gt;# mypy: result is Fail, so .error exists&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;color:#6272a4&#34;&gt;# mypy can detect this branch is unreachable (exhaustiveness)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;_baseresult&#34;&gt;&lt;code&gt;_BaseResult&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#_baseresult&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;_BaseResult&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    error: Exception &lt;span style=&#34;color:#ff79c6&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;None&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This base lets you access &lt;code&gt;result.error&lt;/code&gt; &lt;strong&gt;without narrowing&lt;/strong&gt;:&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
