<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Petrus Nguyễn Thái Học (hoc081098)</title>
        <link>https://portfolio.hoc081098.dev</link>
        <description>Articles on mobile development, backend services, reactive programming, and clean architecture.</description>
        <lastBuildDate>Wed, 17 Jun 2026 12:00:50 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Petrus Nguyễn Thái Học (hoc081098)</title>
            <url>https://portfolio.hoc081098.dev/favicon.ico</url>
            <link>https://portfolio.hoc081098.dev</link>
        </image>
        <copyright>All rights reserved 2026</copyright>
        <item>
            <title><![CDATA[💠 Union types trong C# (bản Tiếng Việt)]]></title>
            <link>https://portfolio.hoc081098.dev/articles/csharp-union-types-vi</link>
            <guid>https://portfolio.hoc081098.dev/articles/csharp-union-types-vi</guid>
            <pubDate>Sun, 05 Apr 2026 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<ul>
<li>
<p>Có sẵn từ <strong>C# 15 / .NET 11 Preview 2</strong>.</p>
</li>
<li>
<p>Docs: <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/union#union-implementation">https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/union#union-implementation</a></p>
</li>
<li>
<p>Proposal: <a href="https://github.com/dotnet/csharplang/blob/main/proposals/unions.md">https://github.com/dotnet/csharplang/blob/main/proposals/unions.md</a></p>
</li>
</ul>
<blockquote>
<p>⚠️ <strong>Disclaimer — Preview feature</strong></p>
<ul>
<li>Yêu cầu <strong>C# 15 / .NET 11 Preview 2</strong> trở lên.</li>
<li>Tính đến .NET 11 Preview 2, <code class="notranslate" translate="no">UnionAttribute</code> và <code class="notranslate" translate="no">IUnion</code> <strong>chưa được include trong runtime</strong>.
Muốn dùng phải tự khai báo trong project (xem mục <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/union#union-implementation">Union implementation</a> trong docs).</li>
<li>Đây là <strong>preview</strong> — API và behavior có thể thay đổi trước khi ship chính thức.</li>
</ul>
</blockquote>
<hr>
<h2>💠 1. Bản chất</h2>
<ul>
<li>
<p>Union types trong C# là <strong>union of types thông qua một wrapper type</strong>.</p>
</li>
<li>
<p>Nó là <strong>type union</strong> — <strong>KHÔNG phải</strong> <em>tagged union</em> hay <em>discriminated union</em> theo nghĩa truyền thống.
Trích trực tiếp từ proposal:</p>
<blockquote>
<p><em>"The proposed unions in C# are unions of <strong>types</strong> and not 'discriminated' or 'tagged'."</em></p>
</blockquote>
<p>Sự khác biệt:</p>
<ul>
<li><strong>Discriminated union</strong> (F#, Haskell): dùng một trường <em>discriminator/tag</em> riêng biệt để phân biệt case.</li>
<li><strong>Type union trong C#</strong>: dùng chính <strong>runtime type</strong> của <code class="notranslate" translate="no">Value</code> làm discriminator — không có trường tag riêng.</li>
<li>Về <em>hành vi</em>, nó tương tự discriminated union ở chỗ pattern matching có thể biết chính xác đang ở case nào, nhưng về <em>cơ chế lưu trữ</em> thì khác.</li>
</ul>
</li>
<li>
<p>Nó vẫn đại diện cho ngữ nghĩa <strong>"HOẶC" giữa các type</strong>, nhưng bản chất của nó là <strong>một cái hộp chứa type cần "hoặc" ở bên trong</strong>.</p>
</li>
</ul>
<p>Ví dụ, ta dùng <strong>Union declaration syntax</strong>:</p>
<pre class="notranslate language-csharp" translate="no"><code class="notranslate language-csharp" translate="no"><span class="token comment">// Union of existing types</span>
<span class="token keyword">public</span> <span class="token return-type class-name">union</span> <span class="token function">Pet</span><span class="token punctuation">(</span>Cat<span class="token punctuation">,</span> Dog<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<p>Được <em>lowered</em> thành:</p>
<pre class="notranslate language-csharp" translate="no"><code class="notranslate language-csharp" translate="no"><span class="token punctuation">[</span><span class="token attribute"><span class="token class-name">Union</span></span><span class="token punctuation">]</span> <span class="token keyword">public</span> <span class="token keyword">struct</span> <span class="token class-name">Pet</span> <span class="token punctuation">:</span> <span class="token type-list"><span class="token class-name">IUnion</span></span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token function">Pet</span><span class="token punctuation">(</span><span class="token class-name">Cat</span> <span class="token keyword">value</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> Value <span class="token operator">=</span> <span class="token keyword">value</span><span class="token punctuation">;</span>
    <span class="token keyword">public</span> <span class="token function">Pet</span><span class="token punctuation">(</span><span class="token class-name">Dog</span> <span class="token keyword">value</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> Value <span class="token operator">=</span> <span class="token keyword">value</span><span class="token punctuation">;</span>
    <span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">object</span><span class="token punctuation">?</span></span> Value <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token range operator">..</span><span class="token punctuation">.</span> <span class="token comment">// original body</span>
<span class="token punctuation">}</span>
</code></pre>
<p>Ta thấy rõ:</p>
<ul>
<li><code class="notranslate" translate="no">Pet</code> chỉ là <strong>một cái hộp</strong>, hoặc chứa <code class="notranslate" translate="no">Cat</code>, hoặc chứa <code class="notranslate" translate="no">Dog</code></li>
<li><code class="notranslate" translate="no">Pet</code> được gọi là <strong>union type</strong></li>
<li><code class="notranslate" translate="no">Cat</code> và <code class="notranslate" translate="no">Dog</code> được gọi là <strong>case types</strong></li>
</ul>
<p>Compiler sẽ đảm bảo <strong>exhaustiveness</strong> khi dùng Union type với pattern matching.</p>
<hr>
<h2>💠 2. Hành vi (union behaviors)</h2>
<h3>💠 2.1. Union conversions</h3>
<p>Có <strong>implicit conversion</strong> từ mỗi <em>case type</em> sang <em>union type</em>:</p>
<pre class="notranslate language-csharp" translate="no"><code class="notranslate language-csharp" translate="no"><span class="token class-name">Pet</span> pet <span class="token operator">=</span> dog<span class="token punctuation">;</span>
<span class="token comment">// becomes</span>
<span class="token class-name">Pet</span> pet <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">Pet</span><span class="token punctuation">(</span>dog<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<h3>💠 2.2. Union matching</h3>
<h4>💠 2.2.1. Apply vào <code class="notranslate" translate="no">Value</code> bên trong</h4>
<ul>
<li>
<p>Khi kiểu dữ liệu của một value là <strong>union type tại compile time</strong>,
thì pattern matching sẽ được áp dụng lên <strong><code class="notranslate" translate="no">Value</code> bên trong union type một cách ngầm định</strong>.</p>
<p>Nghĩa là nó sẽ bị <strong>unwrap ngầm định</strong> thông qua <code class="notranslate" translate="no">.Value</code>, rồi apply pattern lên <code class="notranslate" translate="no">Value</code> đó
<em>(ngoại trừ trường hợp dùng <code class="notranslate" translate="no">var</code> hoặc <code class="notranslate" translate="no">_</code>)</em>.</p>
</li>
</ul>
<p>Ví dụ:</p>
<pre class="notranslate language-csharp" translate="no"><code class="notranslate language-csharp" translate="no"><span class="token return-type class-name">Pet</span> <span class="token function">GetPet</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">Dog</span><span class="token punctuation">(</span><span class="token range operator">..</span><span class="token punctuation">.</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token class-name"><span class="token keyword">var</span></span> description <span class="token operator">=</span> <span class="token function">GetPet</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">switch</span>
<span class="token punctuation">{</span>
    <span class="token return-type class-name">Dog</span> dog <span class="token operator">=&gt;</span> <span class="token interpolation-string"><span class="token string">$"A dog: </span><span class="token interpolation"><span class="token punctuation">{</span><span class="token expression language-csharp">dog<span class="token punctuation">.</span>name</span><span class="token punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">,</span>
    <span class="token return-type class-name">Cat</span> cat <span class="token operator">=&gt;</span> <span class="token interpolation-string"><span class="token string">$"A cat: </span><span class="token interpolation"><span class="token punctuation">{</span><span class="token expression language-csharp">cat<span class="token punctuation">.</span>name</span><span class="token punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">,</span>
    <span class="token comment">// No warning about non-exhaustive switch</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre>
<p>Được <em>lowered</em> thành:</p>
<pre class="notranslate language-csharp" translate="no"><code class="notranslate language-csharp" translate="no"><span class="token function">GetPet</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>Value <span class="token keyword">switch</span> <span class="token punctuation">{</span>
    <span class="token range operator">..</span><span class="token punctuation">.</span>
<span class="token punctuation">}</span>
</code></pre>
<p>Vì compiler biết <code class="notranslate" translate="no">Pet</code> là một <em>union type</em>, nên nó sẽ biết <code class="notranslate" translate="no">switch expression</code> ở trên đã được <strong>exhaustive</strong> rồi.</p>
<h4>💠 2.2.2. Ngoại lệ <code class="notranslate" translate="no">var</code> / <code class="notranslate" translate="no">_</code></h4>
<p>Ngoại lệ là <code class="notranslate" translate="no">var</code> hoặc <code class="notranslate" translate="no">_</code> pattern. Khi đó, pattern sẽ được apply vào <strong>chính <code class="notranslate" translate="no">Pet</code> value</strong>, chứ không phải <code class="notranslate" translate="no">Value</code> của <code class="notranslate" translate="no">Pet</code>.</p>
<pre class="notranslate language-csharp" translate="no"><code class="notranslate language-csharp" translate="no"><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">GetPet</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">is</span> <span class="token class-name"><span class="token keyword">var</span></span> pet<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token range operator">..</span><span class="token punctuation">.</span> <span class="token punctuation">}</span> <span class="token comment">// 'pet' is the union value returned from `GetPet`</span>
</code></pre>
<h4>💠 2.2.3. Check <code class="notranslate" translate="no">null</code></h4>
<p>Khi check 1 biến kiểu nullable union type là <code class="notranslate" translate="no">null</code> hay không,
thì việc check <code class="notranslate" translate="no">null</code> không chỉ apply cho union type value, mà cả <code class="notranslate" translate="no">Value</code> bên trong union type.
(Điều này vẫn tuân thủ Union behavior ở trên - khi compile-time type là union type)</p>
<pre class="notranslate language-csharp" translate="no"><code class="notranslate language-csharp" translate="no"><span class="token class-name">Pet<span class="token punctuation">?</span></span> pet <span class="token operator">=</span> <span class="token range operator">..</span><span class="token punctuation">.</span><span class="token punctuation">;</span>
pet <span class="token keyword">is</span> <span class="token keyword">null</span>
</code></pre>
<p>Được <em>lowered</em> thành:</p>
<pre class="notranslate language-csharp" translate="no"><code class="notranslate language-csharp" translate="no"><span class="token class-name">Pet<span class="token punctuation">?</span></span> pet <span class="token operator">=</span> <span class="token range operator">..</span><span class="token punctuation">.</span><span class="token punctuation">;</span>

<span class="token comment">// Nếu Pet là class union type</span>
pet <span class="token operator">==</span> <span class="token keyword">null</span> <span class="token operator">||</span> pet<span class="token punctuation">.</span>Value <span class="token operator">==</span> <span class="token keyword">null</span>

<span class="token comment">// Nếu Pet là struct union type</span>
pet<span class="token punctuation">.</span>HasValue <span class="token operator">==</span> <span class="token boolean">false</span> <span class="token operator">||</span> pet<span class="token punctuation">.</span><span class="token function">GetValueOrDefault</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>Value <span class="token operator">==</span> <span class="token keyword">null</span>
</code></pre>
<h4>💠 2.2.4. Cạm bẫy</h4>
<pre class="notranslate language-csharp" translate="no"><code class="notranslate language-csharp" translate="no"><span class="token class-name">Pet</span> pet <span class="token operator">=</span> <span class="token range operator">..</span><span class="token punctuation">.</span><span class="token punctuation">;</span>
pet <span class="token keyword">is</span> <span class="token class-name">Pet</span>
</code></pre>
<p>Nó gần như luôn eval thành <code class="notranslate" translate="no">false</code>, vì nó bị <em>lowered</em> thành:</p>
<pre class="notranslate language-csharp" translate="no"><code class="notranslate language-csharp" translate="no">pet<span class="token punctuation">.</span>Value <span class="token keyword">is</span> <span class="token class-name">Pet</span>
</code></pre>
<p>Trong khi <code class="notranslate" translate="no">Value</code> luôn là <code class="notranslate" translate="no">Dog</code> hoặc <code class="notranslate" translate="no">Cat</code> 😂.</p>
<h2>💠 3. Một chỗ rất dễ gây nhầm lẫn: compile-time type đổi thì meaning cũng đổi</h2>
<p>Nếu <code class="notranslate" translate="no">GetPet()</code> trả về kiểu <code class="notranslate" translate="no">object</code>, thì câu chuyện đổi hẳn.</p>
<pre class="notranslate language-csharp" translate="no"><code class="notranslate language-csharp" translate="no"><span class="token return-type class-name"><span class="token keyword">object</span></span> <span class="token function">GetPet</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">Dog</span><span class="token punctuation">(</span><span class="token range operator">..</span><span class="token punctuation">.</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token function">GetPet</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">is</span> <span class="token class-name">Pet</span><span class="token punctuation">;</span>
</code></pre>
<ul>
<li>
<p>Khi đó, <code class="notranslate" translate="no">GetPet() is Pet</code> sẽ eval thành <code class="notranslate" translate="no">true</code> <strong>nếu <code class="notranslate" translate="no">GetPet()</code> trả về một instance của <code class="notranslate" translate="no">Pet</code></strong>.</p>
<ul>
<li>Lúc này, <strong>union behavior sẽ không được áp dụng</strong>, vì <em>compile-time type</em> của giá trị đầu vào là <code class="notranslate" translate="no">object</code>, không phải <code class="notranslate" translate="no">Pet</code>.</li>
<li>Do đó, <code class="notranslate" translate="no">GetPet() is Pet</code> sẽ là <code class="notranslate" translate="no">true</code> theo nghĩa <strong>check type bình thường ở runtime</strong>.</li>
</ul>
</li>
<li>
<p>Lý do là thiết kế hiện tại chỉ bật <strong>union matching</strong> khi <em>compile-time type</em> của giá trị đầu vào đã là <em>union type</em>.
Ngoài trường hợp đó, union chỉ là một type bình thường / value bình thường, không có unwrap <code class="notranslate" translate="no">.Value</code> ngầm.</p>
</li>
<li>
<p>Đây là giải thích trực tiếp từ thảo luận của proposal: <a href="https://github.com/dotnet/csharplang/discussions/10040">https://github.com/dotnet/csharplang/discussions/10040</a></p>
</li>
</ul>
<h2>💠 4. Mental model</h2>
<pre class="notranslate language-csharp" translate="no"><code class="notranslate language-csharp" translate="no"><span class="token return-type class-name">Pet</span> <span class="token function">GetPet</span><span class="token punctuation">(</span><span class="token punctuation">)</span>      <span class="token operator">-&gt;</span>  <span class="token function">GetPet</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">is</span> <span class="token class-name">Pet</span>   <span class="token comment">// union semantics</span>
<span class="token return-type class-name"><span class="token keyword">object</span></span> <span class="token function">GetPet</span><span class="token punctuation">(</span><span class="token punctuation">)</span>   <span class="token operator">-&gt;</span>  <span class="token function">GetPet</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">is</span> <span class="token class-name">Pet</span>   <span class="token comment">// normal runtime type check</span>
</code></pre>
<p>Tức là cùng một cú pháp <code class="notranslate" translate="no">is Pet</code>, nhưng chỉ cần đổi <strong>compile-time type</strong> của biểu thức bên trái là meaning sẽ đổi theo.</p>
<p>Đây chính là lý do nhiều người chê proposal này <strong>"không idempotent"</strong> và dễ gây lú.
Trong discussion <a href="https://github.com/dotnet/csharplang/discussions/10040">https://github.com/dotnet/csharplang/discussions/10040</a> cũng nêu khá rõ rằng <strong>union pattern matching chỉ hoạt động khi compile-time type là union type</strong>.</p>
<hr>
<h2>💠 5. Tổng hợp — snippet dễ nhớ</h2>
<pre class="notranslate language-csharp" translate="no"><code class="notranslate language-csharp" translate="no"><span class="token comment">// ✅ Union declaration syntax (C# 15 / .NET 11 Preview 2+)</span>
<span class="token keyword">public</span> <span class="token return-type class-name">union</span> <span class="token generic-method"><span class="token function">Result</span><span class="token generic class-name"><span class="token punctuation">&lt;</span>T<span class="token punctuation">&gt;</span></span></span><span class="token punctuation">(</span>T<span class="token punctuation">,</span> Exception<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// ✅ Implicit conversion từ case type → union type</span>
<span class="token class-name">Result<span class="token punctuation">&lt;</span><span class="token keyword">int</span><span class="token punctuation">&gt;</span></span> ok    <span class="token operator">=</span> <span class="token number">42</span><span class="token punctuation">;</span>
<span class="token class-name">Result<span class="token punctuation">&lt;</span><span class="token keyword">int</span><span class="token punctuation">&gt;</span></span> fail  <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">Exception</span><span class="token punctuation">(</span><span class="token string">"oops"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// ✅ Pattern matching — exhaustive, không cần fallback</span>
<span class="token class-name"><span class="token keyword">string</span></span> msg <span class="token operator">=</span> ok <span class="token keyword">switch</span>
<span class="token punctuation">{</span>
    <span class="token class-name"><span class="token keyword">int</span></span> <span class="token keyword">value</span>    <span class="token operator">=&gt;</span> <span class="token interpolation-string"><span class="token string">$"Success: </span><span class="token interpolation"><span class="token punctuation">{</span><span class="token expression language-csharp"><span class="token keyword">value</span></span><span class="token punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">,</span>
    <span class="token return-type class-name">Exception</span> ex <span class="token operator">=&gt;</span> <span class="token interpolation-string"><span class="token string">$"Error: </span><span class="token interpolation"><span class="token punctuation">{</span><span class="token expression language-csharp">ex<span class="token punctuation">.</span>Message</span><span class="token punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">,</span>
    <span class="token comment">// No warning — compiler biết đã exhaustive</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token comment">// ✅ null check unwrap cả hai tầng</span>

<span class="token comment">// (union declaration → struct → Result&lt;int&gt;? is Nullable&lt;Result&lt;int&gt;&gt;)</span>
<span class="token class-name">Result<span class="token punctuation">&lt;</span><span class="token keyword">int</span><span class="token punctuation">&gt;</span><span class="token punctuation">?</span></span> maybe <span class="token operator">=</span> <span class="token range operator">..</span><span class="token punctuation">.</span><span class="token punctuation">;</span>

<span class="token comment">// lowered: maybe.HasValue == false || maybe.GetValueOrDefault().Value == null</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>maybe <span class="token keyword">is</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span>

<span class="token comment">// ⚠️ Cạm bẫy: luôn false vì bị unwrap ngầm</span>
<span class="token class-name">Result<span class="token punctuation">&lt;</span><span class="token keyword">int</span><span class="token punctuation">&gt;</span></span> r <span class="token operator">=</span> <span class="token range operator">..</span><span class="token punctuation">.</span><span class="token punctuation">;</span>
r <span class="token keyword">is</span> <span class="token class-name">Result<span class="token punctuation">&lt;</span><span class="token keyword">int</span><span class="token punctuation">&gt;</span></span>                                <span class="token comment">// → r.Value is Result&lt;int&gt; → false!</span>

<span class="token comment">// ⚠️ Compile-time type quyết định behavior</span>
<span class="token class-name">Result<span class="token punctuation">&lt;</span><span class="token keyword">int</span><span class="token punctuation">&gt;</span></span>  r2 <span class="token operator">=</span> <span class="token range operator">..</span><span class="token punctuation">.</span><span class="token punctuation">;</span>  r2 <span class="token keyword">is</span> <span class="token class-name"><span class="token keyword">int</span></span>               <span class="token comment">// union semantics ✅</span>
<span class="token class-name"><span class="token keyword">object</span></span>       r3 <span class="token operator">=</span> <span class="token range operator">..</span><span class="token punctuation">.</span><span class="token punctuation">;</span>  r3 <span class="token keyword">is</span> <span class="token class-name">Result<span class="token punctuation">&lt;</span><span class="token keyword">int</span><span class="token punctuation">&gt;</span></span>       <span class="token comment">// runtime type check thường ⚠️</span>
</code></pre>]]></content:encoded>
            <author>hoc081098@gmail.com (Petrus Nguyễn Thái Học (hoc081098))</author>
        </item>
        <item>
            <title><![CDATA[💠 Union Types in C#]]></title>
            <link>https://portfolio.hoc081098.dev/articles/csharp-union-types</link>
            <guid>https://portfolio.hoc081098.dev/articles/csharp-union-types</guid>
            <pubDate>Sun, 05 Apr 2026 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<ul>
<li>
<p>Available from <strong>C# 15 / .NET 11 Preview 2</strong>.</p>
</li>
<li>
<p>Docs: <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/union#union-implementation">https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/union#union-implementation</a></p>
</li>
<li>
<p>Proposal: <a href="https://github.com/dotnet/csharplang/blob/main/proposals/unions.md">https://github.com/dotnet/csharplang/blob/main/proposals/unions.md</a></p>
</li>
</ul>
<blockquote>
<p>⚠️ <strong>Disclaimer — Preview feature</strong></p>
<ul>
<li>Requires <strong>C# 15 / .NET 11 Preview 2</strong> or later.</li>
<li>As of .NET 11 Preview 2, <code class="notranslate" translate="no">UnionAttribute</code> and <code class="notranslate" translate="no">IUnion</code> are <strong>not yet included in the runtime</strong>.
You must declare them manually in your project (see <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/union#union-implementation">Union implementation</a> in the docs).</li>
<li>This is a <strong>preview</strong> — APIs and behaviors may change before the final release.</li>
</ul>
</blockquote>
<hr>
<h2>💠 1. What is a Union Type?</h2>
<ul>
<li>
<p>A union type in C# is a <strong>union of types via a wrapper type</strong>.</p>
</li>
<li>
<p>It is a <strong>type union</strong> — <strong>NOT</strong> a <em>tagged union</em> or <em>discriminated union</em> in the traditional sense.
Quoted directly from the proposal:</p>
<blockquote>
<p><em>"The proposed unions in C# are unions of <strong>types</strong> and not 'discriminated' or 'tagged'."</em></p>
</blockquote>
<p>The distinction:</p>
<ul>
<li><strong>Discriminated union</strong> (F#, Haskell): uses a dedicated <em>discriminator/tag</em> field to tell cases apart.</li>
<li><strong>Type union in C#</strong>: uses the <strong>runtime type</strong> of <code class="notranslate" translate="no">Value</code> itself as the discriminator — no separate tag field.</li>
<li>In terms of <em>behavior</em>, it resembles a discriminated union in that pattern matching can identify the exact case, but the <em>storage mechanism</em> is different.</li>
</ul>
</li>
<li>
<p>It still expresses the <strong>"OR" semantics between types</strong>, but its underlying form is <strong>a box that holds one of the possible types</strong>.</p>
</li>
</ul>
<p>For example, using the <strong>Union declaration syntax</strong>:</p>
<pre class="notranslate language-csharp" translate="no"><code class="notranslate language-csharp" translate="no"><span class="token comment">// Union of existing types</span>
<span class="token keyword">public</span> <span class="token return-type class-name">union</span> <span class="token function">Pet</span><span class="token punctuation">(</span>Cat<span class="token punctuation">,</span> Dog<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<p>Is <em>lowered</em> to:</p>
<pre class="notranslate language-csharp" translate="no"><code class="notranslate language-csharp" translate="no"><span class="token punctuation">[</span><span class="token attribute"><span class="token class-name">Union</span></span><span class="token punctuation">]</span> <span class="token keyword">public</span> <span class="token keyword">struct</span> <span class="token class-name">Pet</span> <span class="token punctuation">:</span> <span class="token type-list"><span class="token class-name">IUnion</span></span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token function">Pet</span><span class="token punctuation">(</span><span class="token class-name">Cat</span> <span class="token keyword">value</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> Value <span class="token operator">=</span> <span class="token keyword">value</span><span class="token punctuation">;</span>
    <span class="token keyword">public</span> <span class="token function">Pet</span><span class="token punctuation">(</span><span class="token class-name">Dog</span> <span class="token keyword">value</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> Value <span class="token operator">=</span> <span class="token keyword">value</span><span class="token punctuation">;</span>
    <span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">object</span><span class="token punctuation">?</span></span> Value <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token range operator">..</span><span class="token punctuation">.</span> <span class="token comment">// original body</span>
<span class="token punctuation">}</span>
</code></pre>
<p>Clearly:</p>
<ul>
<li><code class="notranslate" translate="no">Pet</code> is just <strong>a box</strong> that holds either a <code class="notranslate" translate="no">Cat</code> or a <code class="notranslate" translate="no">Dog</code></li>
<li><code class="notranslate" translate="no">Pet</code> is called the <strong>union type</strong></li>
<li><code class="notranslate" translate="no">Cat</code> and <code class="notranslate" translate="no">Dog</code> are called <strong>case types</strong></li>
</ul>
<p>The compiler guarantees <strong>exhaustiveness</strong> when using a union type with pattern matching.</p>
<hr>
<h2>💠 2. Union Behaviors</h2>
<h3>💠 2.1. Union Conversions</h3>
<p>There is an <strong>implicit conversion</strong> from each <em>case type</em> to the <em>union type</em>:</p>
<pre class="notranslate language-csharp" translate="no"><code class="notranslate language-csharp" translate="no"><span class="token class-name">Pet</span> pet <span class="token operator">=</span> dog<span class="token punctuation">;</span>
<span class="token comment">// becomes</span>
<span class="token class-name">Pet</span> pet <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">Pet</span><span class="token punctuation">(</span>dog<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<h3>💠 2.2. Union Matching</h3>
<h4>💠 2.2.1. Applied to the Inner <code class="notranslate" translate="no">Value</code></h4>
<ul>
<li>
<p>When the compile-time type of a value is a <strong>union type</strong>,
pattern matching is implicitly applied to the <strong><code class="notranslate" translate="no">Value</code> inside the union type</strong>.</p>
<p>In other words, the union is <strong>implicitly unwrapped</strong> via <code class="notranslate" translate="no">.Value</code>, and the pattern is applied to that <code class="notranslate" translate="no">Value</code>
<em>(except when using <code class="notranslate" translate="no">var</code> or <code class="notranslate" translate="no">_</code>)</em>.</p>
</li>
</ul>
<p>Example:</p>
<pre class="notranslate language-csharp" translate="no"><code class="notranslate language-csharp" translate="no"><span class="token return-type class-name">Pet</span> <span class="token function">GetPet</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">Dog</span><span class="token punctuation">(</span><span class="token range operator">..</span><span class="token punctuation">.</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token class-name"><span class="token keyword">var</span></span> description <span class="token operator">=</span> <span class="token function">GetPet</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">switch</span>
<span class="token punctuation">{</span>
    <span class="token return-type class-name">Dog</span> dog <span class="token operator">=&gt;</span> <span class="token interpolation-string"><span class="token string">$"A dog: </span><span class="token interpolation"><span class="token punctuation">{</span><span class="token expression language-csharp">dog<span class="token punctuation">.</span>name</span><span class="token punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">,</span>
    <span class="token return-type class-name">Cat</span> cat <span class="token operator">=&gt;</span> <span class="token interpolation-string"><span class="token string">$"A cat: </span><span class="token interpolation"><span class="token punctuation">{</span><span class="token expression language-csharp">cat<span class="token punctuation">.</span>name</span><span class="token punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">,</span>
    <span class="token comment">// No warning about non-exhaustive switch</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre>
<p>Is <em>lowered</em> to:</p>
<pre class="notranslate language-csharp" translate="no"><code class="notranslate language-csharp" translate="no"><span class="token function">GetPet</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>Value <span class="token keyword">switch</span> <span class="token punctuation">{</span>
    <span class="token range operator">..</span><span class="token punctuation">.</span>
<span class="token punctuation">}</span>
</code></pre>
<p>Because the compiler knows <code class="notranslate" translate="no">Pet</code> is a <em>union type</em>, it knows the <code class="notranslate" translate="no">switch expression</code> above is already <strong>exhaustive</strong>.</p>
<h4>💠 2.2.2. Exception: <code class="notranslate" translate="no">var</code> / <code class="notranslate" translate="no">_</code></h4>
<p>The <code class="notranslate" translate="no">var</code> or <code class="notranslate" translate="no">_</code> patterns are exceptions. In these cases, the pattern is applied to <strong>the <code class="notranslate" translate="no">Pet</code> value itself</strong>, not its <code class="notranslate" translate="no">Value</code>.</p>
<pre class="notranslate language-csharp" translate="no"><code class="notranslate language-csharp" translate="no"><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">GetPet</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">is</span> <span class="token class-name"><span class="token keyword">var</span></span> pet<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token range operator">..</span><span class="token punctuation">.</span> <span class="token punctuation">}</span> <span class="token comment">// 'pet' is the union value returned from `GetPet`</span>
</code></pre>
<h4>💠 2.2.3. Null Checking</h4>
<p>When checking whether a nullable union type variable is <code class="notranslate" translate="no">null</code>,
the <code class="notranslate" translate="no">null</code> check applies not only to the union value itself but also to the <code class="notranslate" translate="no">Value</code> inside it.
(This still follows the Union behavior above — when the compile-time type is a union type.)</p>
<pre class="notranslate language-csharp" translate="no"><code class="notranslate language-csharp" translate="no"><span class="token class-name">Pet<span class="token punctuation">?</span></span> pet <span class="token operator">=</span> <span class="token range operator">..</span><span class="token punctuation">.</span><span class="token punctuation">;</span>
pet <span class="token keyword">is</span> <span class="token keyword">null</span>
</code></pre>
<p>Is <em>lowered</em> to:</p>
<pre class="notranslate language-csharp" translate="no"><code class="notranslate language-csharp" translate="no"><span class="token class-name">Pet<span class="token punctuation">?</span></span> pet <span class="token operator">=</span> <span class="token range operator">..</span><span class="token punctuation">.</span><span class="token punctuation">;</span>

<span class="token comment">// If Pet is a class union type</span>
pet <span class="token operator">==</span> <span class="token keyword">null</span> <span class="token operator">||</span> pet<span class="token punctuation">.</span>Value <span class="token operator">==</span> <span class="token keyword">null</span>

<span class="token comment">// If Pet is a struct union type</span>
pet<span class="token punctuation">.</span>HasValue <span class="token operator">==</span> <span class="token boolean">false</span> <span class="token operator">||</span> pet<span class="token punctuation">.</span><span class="token function">GetValueOrDefault</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>Value <span class="token operator">==</span> <span class="token keyword">null</span>
</code></pre>
<h4>💠 2.2.4. The Trap</h4>
<pre class="notranslate language-csharp" translate="no"><code class="notranslate language-csharp" translate="no"><span class="token class-name">Pet</span> pet <span class="token operator">=</span> <span class="token range operator">..</span><span class="token punctuation">.</span><span class="token punctuation">;</span>
pet <span class="token keyword">is</span> <span class="token class-name">Pet</span>
</code></pre>
<p>This almost always evaluates to <code class="notranslate" translate="no">false</code>, because it is <em>lowered</em> to:</p>
<pre class="notranslate language-csharp" translate="no"><code class="notranslate language-csharp" translate="no">pet<span class="token punctuation">.</span>Value <span class="token keyword">is</span> <span class="token class-name">Pet</span>
</code></pre>
<p>While <code class="notranslate" translate="no">Value</code> is always <code class="notranslate" translate="no">Dog</code> or <code class="notranslate" translate="no">Cat</code> 😂.</p>
<hr>
<h2>💠 3. A Common Source of Confusion: Changing the Compile-Time Type Changes the Meaning</h2>
<p>If <code class="notranslate" translate="no">GetPet()</code> returns <code class="notranslate" translate="no">object</code>, the story changes entirely.</p>
<pre class="notranslate language-csharp" translate="no"><code class="notranslate language-csharp" translate="no"><span class="token return-type class-name"><span class="token keyword">object</span></span> <span class="token function">GetPet</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">Dog</span><span class="token punctuation">(</span><span class="token range operator">..</span><span class="token punctuation">.</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token function">GetPet</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">is</span> <span class="token class-name">Pet</span><span class="token punctuation">;</span>
</code></pre>
<ul>
<li>
<p>Here, <code class="notranslate" translate="no">GetPet() is Pet</code> evaluates to <code class="notranslate" translate="no">true</code> <strong>if <code class="notranslate" translate="no">GetPet()</code> returns an instance of <code class="notranslate" translate="no">Pet</code></strong>.</p>
<ul>
<li><strong>Union behavior is not applied</strong>, because the <em>compile-time type</em> of the input is <code class="notranslate" translate="no">object</code>, not <code class="notranslate" translate="no">Pet</code>.</li>
<li>Therefore, <code class="notranslate" translate="no">GetPet() is Pet</code> is <code class="notranslate" translate="no">true</code> in the sense of a <strong>normal runtime type check</strong>.</li>
</ul>
</li>
<li>
<p>The reason is that the current design only activates <strong>union matching</strong> when the <em>compile-time type</em> of the input is already a <em>union type</em>.
Outside of that case, a union is just a regular type / regular value — no implicit <code class="notranslate" translate="no">.Value</code> unwrapping.</p>
</li>
<li>
<p>This is explained directly in the proposal discussion: <a href="https://github.com/dotnet/csharplang/discussions/10040">https://github.com/dotnet/csharplang/discussions/10040</a></p>
</li>
</ul>
<hr>
<h2>💠 4. Mental Model</h2>
<pre class="notranslate language-csharp" translate="no"><code class="notranslate language-csharp" translate="no"><span class="token return-type class-name">Pet</span> <span class="token function">GetPet</span><span class="token punctuation">(</span><span class="token punctuation">)</span>      <span class="token operator">-&gt;</span>  <span class="token function">GetPet</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">is</span> <span class="token class-name">Pet</span>   <span class="token comment">// union semantics</span>
<span class="token return-type class-name"><span class="token keyword">object</span></span> <span class="token function">GetPet</span><span class="token punctuation">(</span><span class="token punctuation">)</span>   <span class="token operator">-&gt;</span>  <span class="token function">GetPet</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">is</span> <span class="token class-name">Pet</span>   <span class="token comment">// normal runtime type check</span>
</code></pre>
<p>The same syntax <code class="notranslate" translate="no">is Pet</code>, but simply changing the <strong>compile-time type</strong> of the left-hand expression changes the meaning entirely.</p>
<p>This is exactly why many people criticize this proposal as <strong>"not idempotent"</strong> and easy to get confused by.
The discussion at <a href="https://github.com/dotnet/csharplang/discussions/10040">https://github.com/dotnet/csharplang/discussions/10040</a> also makes it clear that <strong>union pattern matching only activates when the compile-time type is a union type</strong>.</p>
<hr>
<h2>💠 5. Summary — Quick Reference Snippet</h2>
<pre class="notranslate language-csharp" translate="no"><code class="notranslate language-csharp" translate="no"><span class="token comment">// ✅ Union declaration syntax (C# 15 / .NET 11 Preview 2+)</span>
<span class="token keyword">public</span> <span class="token return-type class-name">union</span> <span class="token generic-method"><span class="token function">Result</span><span class="token generic class-name"><span class="token punctuation">&lt;</span>T<span class="token punctuation">&gt;</span></span></span><span class="token punctuation">(</span>T<span class="token punctuation">,</span> Exception<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// ✅ Implicit conversion from case type → union type</span>
<span class="token class-name">Result<span class="token punctuation">&lt;</span><span class="token keyword">int</span><span class="token punctuation">&gt;</span></span> ok    <span class="token operator">=</span> <span class="token number">42</span><span class="token punctuation">;</span>
<span class="token class-name">Result<span class="token punctuation">&lt;</span><span class="token keyword">int</span><span class="token punctuation">&gt;</span></span> fail  <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">Exception</span><span class="token punctuation">(</span><span class="token string">"oops"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// ✅ Pattern matching — exhaustive, no fallback needed</span>
<span class="token class-name"><span class="token keyword">string</span></span> msg <span class="token operator">=</span> ok <span class="token keyword">switch</span>
<span class="token punctuation">{</span>
    <span class="token class-name"><span class="token keyword">int</span></span> <span class="token keyword">value</span>    <span class="token operator">=&gt;</span> <span class="token interpolation-string"><span class="token string">$"Success: </span><span class="token interpolation"><span class="token punctuation">{</span><span class="token expression language-csharp"><span class="token keyword">value</span></span><span class="token punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">,</span>
    <span class="token return-type class-name">Exception</span> ex <span class="token operator">=&gt;</span> <span class="token interpolation-string"><span class="token string">$"Error: </span><span class="token interpolation"><span class="token punctuation">{</span><span class="token expression language-csharp">ex<span class="token punctuation">.</span>Message</span><span class="token punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">,</span>
    <span class="token comment">// No warning — compiler knows all cases are covered</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token comment">// ✅ Null check unwraps both layers</span>
<span class="token comment">// (union declaration → struct → Result&lt;int&gt;? is Nullable&lt;Result&lt;int&gt;&gt;)</span>
<span class="token class-name">Result<span class="token punctuation">&lt;</span><span class="token keyword">int</span><span class="token punctuation">&gt;</span><span class="token punctuation">?</span></span> maybe <span class="token operator">=</span> <span class="token range operator">..</span><span class="token punctuation">.</span><span class="token punctuation">;</span>
<span class="token comment">// lowered: maybe.HasValue == false || maybe.GetValueOrDefault().Value == null</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>maybe <span class="token keyword">is</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span>

<span class="token comment">// ⚠️ Trap: always false due to implicit unwrapping</span>
<span class="token class-name">Result<span class="token punctuation">&lt;</span><span class="token keyword">int</span><span class="token punctuation">&gt;</span></span> r <span class="token operator">=</span> <span class="token range operator">..</span><span class="token punctuation">.</span><span class="token punctuation">;</span>
r <span class="token keyword">is</span> <span class="token class-name">Result<span class="token punctuation">&lt;</span><span class="token keyword">int</span><span class="token punctuation">&gt;</span></span>                            <span class="token comment">// → r.Value is Result&lt;int&gt; → false!</span>

<span class="token comment">// ⚠️ Compile-time type determines behavior</span>
<span class="token class-name">Result<span class="token punctuation">&lt;</span><span class="token keyword">int</span><span class="token punctuation">&gt;</span></span>  r2 <span class="token operator">=</span> <span class="token range operator">..</span><span class="token punctuation">.</span><span class="token punctuation">;</span>  r2 <span class="token keyword">is</span> <span class="token class-name"><span class="token keyword">int</span></span>           <span class="token comment">// union semantics ✅</span>
<span class="token class-name"><span class="token keyword">object</span></span>       r3 <span class="token operator">=</span> <span class="token range operator">..</span><span class="token punctuation">.</span><span class="token punctuation">;</span>  r3 <span class="token keyword">is</span> <span class="token class-name">Result<span class="token punctuation">&lt;</span><span class="token keyword">int</span><span class="token punctuation">&gt;</span></span>   <span class="token comment">// normal runtime type check ⚠️</span>
</code></pre>]]></content:encoded>
            <author>hoc081098@gmail.com (Petrus Nguyễn Thái Học (hoc081098))</author>
        </item>
        <item>
            <title><![CDATA[Grouping trong Kotlin (Grouping in Kotlin)]]></title>
            <link>https://portfolio.hoc081098.dev/articles/grouping-in-kotlin</link>
            <guid>https://portfolio.hoc081098.dev/articles/grouping-in-kotlin</guid>
            <pubDate>Sun, 28 May 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<blockquote>
<p><em>Source code</em>: <a href="https://github.com/hoc081098/hoc081098/blob/master/notes/grouping_in_kotlin_vi_VN.kt">https://github.com/hoc081098/hoc081098/blob/master/notes/grouping_in_kotlin_vi_VN.kt</a></p>
</blockquote>
<h2>Đặt vấn đề</h2>
<p>Giả sử chúng ta có một bài toán nhỏ như sau:</p>
<ul>
<li>
<p>Input: cho danh sách các sinh viên, mỗi sinh viên có các thuộc tính như sau</p>
<ul>
<li><code class="notranslate" translate="no">id</code>: mã sinh viên.</li>
<li><code class="notranslate" translate="no">name</code>: tên sinh viên.</li>
<li><code class="notranslate" translate="no">classId</code>: mã lớp.</li>
<li><code class="notranslate" translate="no">avgScore</code>: điểm trung bình.</li>
</ul>
</li>
<li>
<p>Output: cho biết sinh viên có điểm trung bình cao nhất của mỗi lớp, kết quả được sắp xếp theo thứ tự tăng dần của mã lớp.</p>
</li>
</ul>
<pre class="notranslate language-kotlin" translate="no"><code class="notranslate language-kotlin" translate="no"><span class="token comment">// Input: A list of Students, each Student has id, name, class_id, avg_score.</span>
<span class="token comment">// Output: Map of class_id to the student with the highest avg_score in that class.</span>
<span class="token comment">//         Keys should be sorted in ascending order.</span>
<span class="token comment">// Example:</span>
<span class="token comment">// Input: [</span>
<span class="token comment">//   Student("1", "A", 1, 8.0),</span>
<span class="token comment">//   Student("2", "B", 1, 9.0),</span>
<span class="token comment">//   Student("3", "A", 2, 7.0),</span>
<span class="token comment">// ]</span>
<span class="token comment">// Output: {</span>
<span class="token comment">//   1: Student("2", "B", 1, 9.0),</span>
<span class="token comment">//   2: Student("3", "A", 2, 7.0),</span>
<span class="token comment">// }</span>

<span class="token keyword">data</span> <span class="token keyword">class</span> <span class="token function">Student</span><span class="token punctuation">(</span>
  <span class="token keyword">val</span> id<span class="token operator">:</span> String<span class="token punctuation">,</span>
  <span class="token keyword">val</span> name<span class="token operator">:</span> String<span class="token punctuation">,</span>
  <span class="token keyword">val</span> classId<span class="token operator">:</span> Int<span class="token punctuation">,</span>
  <span class="token keyword">val</span> avgScore<span class="token operator">:</span> Double
<span class="token punctuation">)</span>

<span class="token annotation builtin">@JvmField</span>
<span class="token keyword">val</span> compareByAvgScore <span class="token operator">=</span> compareBy<span class="token operator">&lt;</span>Student<span class="token operator">&gt;</span> <span class="token punctuation">{</span> it<span class="token punctuation">.</span>avgScore <span class="token punctuation">}</span>

<span class="token comment">// (TreeMap take O(log n) time in the worst case to get/put).</span>
<span class="token keyword">typealias</span> Output <span class="token operator">=</span> SortedMap<span class="token operator">&lt;</span>Int<span class="token punctuation">,</span> Student<span class="token operator">&gt;</span>

<span class="token keyword">fun</span> <span class="token function">solution</span><span class="token punctuation">(</span>students<span class="token operator">:</span> List<span class="token operator">&lt;</span>Student<span class="token operator">&gt;</span><span class="token punctuation">)</span><span class="token operator">:</span> Output <span class="token operator">=</span> <span class="token function">TODO</span><span class="token punctuation">(</span><span class="token punctuation">)</span>

<span class="token keyword">fun</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">val</span> students <span class="token operator">=</span> <span class="token function">listOf</span><span class="token punctuation">(</span>
    <span class="token function">Student</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"1"</span></span><span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"A"</span></span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">8.0</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token function">Student</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"2"</span></span><span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"B"</span></span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">9.0</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token function">Student</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"3"</span></span><span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"A"</span></span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">7.0</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  <span class="token punctuation">)</span>
  <span class="token keyword">val</span> output <span class="token operator">=</span> <span class="token function">mapOf</span><span class="token punctuation">(</span>
    <span class="token number">1</span> <span class="token keyword">to</span> <span class="token function">Student</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"2"</span></span><span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"B"</span></span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">9.0</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token number">2</span> <span class="token keyword">to</span> <span class="token function">Student</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"3"</span></span><span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"A"</span></span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">7.0</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  <span class="token punctuation">)</span>
  <span class="token function">check</span><span class="token punctuation">(</span><span class="token function">solution</span><span class="token punctuation">(</span>students<span class="token punctuation">)</span> <span class="token operator">==</span> output<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre>
<h2>Solution 1: Sử dụng vòng lặp</h2>
<p>Cách này ổn về mặt thời gian và không gian, nhưng cách viết khá dài dòng và khó đọc, và theo imperactive style
(for loop + if statement).</p>
<pre class="notranslate language-kotlin" translate="no"><code class="notranslate language-kotlin" translate="no"><span class="token comment">// Time complexity (worst case) = n * (O(log n) + O(log n)) = O(2 * n * log n).</span>
<span class="token comment">// Space: 1 TreeMap.</span>
<span class="token comment">// ----</span>
<span class="token comment">// Pros: good in terms of time complexity and space complexity.</span>
<span class="token comment">// Cons: imperative style.</span>
<span class="token keyword">fun</span> <span class="token function">solution1</span><span class="token punctuation">(</span>students<span class="token operator">:</span> List<span class="token operator">&lt;</span>Student<span class="token operator">&gt;</span><span class="token punctuation">)</span><span class="token operator">:</span> Output <span class="token punctuation">{</span>
  <span class="token keyword">val</span> result <span class="token operator">=</span> sortedMapOf<span class="token operator">&lt;</span>Int<span class="token punctuation">,</span> Student<span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  <span class="token keyword">for</span> <span class="token punctuation">(</span>student <span class="token keyword">in</span> students<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">val</span> current <span class="token operator">=</span> result<span class="token punctuation">[</span>student<span class="token punctuation">.</span>classId<span class="token punctuation">]</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>current <span class="token operator">==</span> <span class="token keyword">null</span> <span class="token operator">||</span> student<span class="token punctuation">.</span>avgScore <span class="token operator">&gt;</span> current<span class="token punctuation">.</span>avgScore<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      result<span class="token punctuation">[</span>student<span class="token punctuation">.</span>classId<span class="token punctuation">]</span> <span class="token operator">=</span> student
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
  <span class="token keyword">return</span> result
<span class="token punctuation">}</span>
</code></pre>
<h2>Solution 2: Sử dụng <code class="notranslate" translate="no">groupBy</code> và <code class="notranslate" translate="no">mapValues</code></h2>
<p>Cách này sử dụng functional style, nhưng không tốt về mặt thời gian và không gian.
Nó phải tạo ra 1 HashMap trung gian, và một TreeMap (SortedMap) để lưu kết quả.
Ngoài ra, nó cũng phải duyệt 3 lần, 1 lần để groupBy, 1 lần để mapValues, trong mỗi lần mapValues lại phải duyệt để tìm max.</p>
<pre class="notranslate language-kotlin" translate="no"><code class="notranslate language-kotlin" translate="no"><span class="token comment">// Time complexity (worst case)</span>
<span class="token comment">//     - groupByTo: O(n * ( O(1) + O(n) )) = O(n^2)</span>
<span class="token comment">//     - mapValuesTo: O(n * ( O(log n) + O(n) )) = O(n^2 * log n)</span>
<span class="token comment">//     - total: O(n^2 * log n)</span>
<span class="token comment">// Space: 1 HashMap + 1 TreeMap</span>
<span class="token comment">// ----</span>
<span class="token comment">// Pros: functional style.</span>
<span class="token comment">// Cons: bad in terms of time complexity and space complexity.</span>
<span class="token keyword">fun</span> <span class="token function">solution2</span><span class="token punctuation">(</span>students<span class="token operator">:</span> List<span class="token operator">&lt;</span>Student<span class="token operator">&gt;</span><span class="token punctuation">)</span><span class="token operator">:</span> SortedMap<span class="token operator">&lt;</span>Int<span class="token punctuation">,</span> Student<span class="token operator">&gt;</span> <span class="token operator">=</span> students
  <span class="token punctuation">.</span><span class="token function">groupByTo</span><span class="token punctuation">(</span><span class="token function">hashMapOf</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> it<span class="token punctuation">.</span>classId <span class="token punctuation">}</span>
  <span class="token punctuation">.</span><span class="token function">mapValuesTo</span><span class="token punctuation">(</span><span class="token function">sortedMapOf</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">(</span>_<span class="token punctuation">,</span> v<span class="token punctuation">)</span> <span class="token operator">-&gt;</span> v<span class="token punctuation">.</span><span class="token function">maxBy</span> <span class="token punctuation">{</span> it<span class="token punctuation">.</span>avgScore <span class="token punctuation">}</span> <span class="token punctuation">}</span>
</code></pre>
<h2>Solution 3: Sử dụng <code class="notranslate" translate="no">groupingBy</code> và <code class="notranslate" translate="no">reduce</code></h2>
<pre class="notranslate language-kotlin" translate="no"><code class="notranslate language-kotlin" translate="no"><span class="token comment">// Time complexity (worst case)</span>
<span class="token comment">//     - groupingBy: O(1) (just returns a Grouping object)</span>
<span class="token comment">//     - reduceTo: O(n * ( O(log n) + O(log n) )) = O(n * log n)</span>
<span class="token comment">//     - total: O(2 * n * log n)</span>
<span class="token comment">// Space: 1 TreeMap (Grouping object is small).</span>
<span class="token comment">// ----</span>
<span class="token comment">// Pros: BEST. Functional style, good in terms of time complexity and space complexity.</span>
<span class="token comment">// Cons: NO.</span>
<span class="token keyword">fun</span> <span class="token function">solution3</span><span class="token punctuation">(</span>students<span class="token operator">:</span> List<span class="token operator">&lt;</span>Student<span class="token operator">&gt;</span><span class="token punctuation">)</span><span class="token operator">:</span> SortedMap<span class="token operator">&lt;</span>Int<span class="token punctuation">,</span> Student<span class="token operator">&gt;</span> <span class="token operator">=</span> students
  <span class="token punctuation">.</span><span class="token function">groupingBy</span> <span class="token punctuation">{</span> it<span class="token punctuation">.</span>classId <span class="token punctuation">}</span>
  <span class="token punctuation">.</span><span class="token function">reduceTo</span><span class="token punctuation">(</span><span class="token function">sortedMapOf</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> _<span class="token punctuation">,</span> accumulator<span class="token punctuation">,</span> element <span class="token operator">-&gt;</span> <span class="token function">maxOf</span><span class="token punctuation">(</span>accumulator<span class="token punctuation">,</span> element<span class="token punctuation">,</span> compareByAvgScore<span class="token punctuation">)</span> <span class="token punctuation">}</span>
</code></pre>
<p>Cách này là tốt nhất cho đến hiện tại :), nó sử dụng functional style, và tốt về mặt thời gian và không gian.</p>
<ul>
<li>
<p>Bản chất, <code class="notranslate" translate="no">groupingBy</code> sẽ tạo và return 1 object <code class="notranslate" translate="no">Grouping</code> để lưu trữ <code class="notranslate" translate="no">Iterator</code> và <code class="notranslate" translate="no">Key Selector</code> để thực hiện việc <code class="notranslate" translate="no">reduce</code>/<code class="notranslate" translate="no">aggregate</code> sau đó.</p>
<pre class="notranslate language-kotlin" translate="no"><code class="notranslate language-kotlin" translate="no"><span class="token annotation builtin">@SinceKotlin</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"1.1"</span></span><span class="token punctuation">)</span> <span class="token keyword">public</span> <span class="token keyword">interface</span> Grouping<span class="token operator">&lt;</span>T<span class="token punctuation">,</span> <span class="token keyword">out</span> K<span class="token operator">&gt;</span> <span class="token punctuation">{</span>
    <span class="token keyword">fun</span> <span class="token function">sourceIterator</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> Iterator<span class="token operator">&lt;</span>T<span class="token operator">&gt;</span>
    <span class="token keyword">fun</span> <span class="token function">keyOf</span><span class="token punctuation">(</span>element<span class="token operator">:</span> T<span class="token punctuation">)</span><span class="token operator">:</span> K
<span class="token punctuation">}</span>
</code></pre>
<p><code class="notranslate" translate="no">Grouping</code> không thực hiện việc duyệt, <code class="notranslate" translate="no">reduce</code>/<code class="notranslate" translate="no">aggregate</code> ngay lập tức, mà nó chỉ lữu trữ những cái cần thiết để thực hiện việc <code class="notranslate" translate="no">aggregate</code> sau này.
Đó là cơ chế <strong>LAZY</strong>, tương tự như <code class="notranslate" translate="no">Sequence</code> của Koltin, <code class="notranslate" translate="no">Stream</code> của Java, <code class="notranslate" translate="no">Observable</code> của RxJava, <code class="notranslate" translate="no">Flow</code> của Kotlin Coroutines, ...</p>
</li>
<li>
<p>Sau đó, <code class="notranslate" translate="no">reduceTo</code> sẽ duyệt qua <code class="notranslate" translate="no">Grouping</code> object, và thực hiện việc <code class="notranslate" translate="no">reduce</code>/<code class="notranslate" translate="no">aggregate</code> trên từng <code class="notranslate" translate="no">Group</code> xác định bởi <code class="notranslate" translate="no">Key Selector</code>.
Hàm <code class="notranslate" translate="no">reduceTo</code> trên <code class="notranslate" translate="no">Grouping</code> có cơ chế khá giống trên <code class="notranslate" translate="no">Iterable</code>, nhưng nó không thực thi <code class="notranslate" translate="no">operation</code> trên toàn bộ <code class="notranslate" translate="no">Iterable</code>,
mà nó thực thi <code class="notranslate" translate="no">operation</code> trên từng <code class="notranslate" translate="no">Group</code> xác định bởi <code class="notranslate" translate="no">Key Selector</code>.
Bản chất như sau (không giống source của stdlib 100%, vì lược bỏ những thứ không cần thiết):</p>
</li>
</ul>
<pre class="notranslate language-kotlin" translate="no"><code class="notranslate language-kotlin" translate="no"><span class="token keyword">inline</span> <span class="token keyword">fun</span> <span class="token operator">&lt;</span>S<span class="token punctuation">,</span> T <span class="token operator">:</span> S<span class="token punctuation">,</span> K<span class="token punctuation">,</span> M <span class="token operator">:</span> MutableMap<span class="token operator">&lt;</span><span class="token keyword">in</span> K<span class="token punctuation">,</span> S<span class="token operator">&gt;</span><span class="token operator">&gt;</span> Grouping<span class="token operator">&lt;</span>T<span class="token punctuation">,</span> K<span class="token operator">&gt;</span><span class="token punctuation">.</span><span class="token function">reduceTo</span><span class="token punctuation">(</span>
  destination<span class="token operator">:</span> M<span class="token punctuation">,</span>
  operation<span class="token operator">:</span> <span class="token punctuation">(</span>key<span class="token operator">:</span> K<span class="token punctuation">,</span> accumulator<span class="token operator">:</span> S<span class="token punctuation">,</span> element<span class="token operator">:</span> T<span class="token punctuation">)</span> <span class="token operator">-&gt;</span> S
<span class="token punctuation">)</span><span class="token operator">:</span> M <span class="token punctuation">{</span>
  <span class="token keyword">for</span> <span class="token punctuation">(</span>element <span class="token keyword">in</span> <span class="token function">sourceIterator</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">val</span> key <span class="token operator">=</span> <span class="token function">keyOf</span><span class="token punctuation">(</span>element<span class="token punctuation">)</span>
    <span class="token keyword">val</span> accumulator <span class="token operator">=</span> destination<span class="token punctuation">[</span>key<span class="token punctuation">]</span>
    destination<span class="token punctuation">[</span>key<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>accumulator <span class="token operator">==</span> <span class="token keyword">null</span> <span class="token operator">&amp;&amp;</span> <span class="token operator">!</span>destination<span class="token punctuation">.</span><span class="token function">containsKey</span><span class="token punctuation">(</span>key<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      element
    <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
      <span class="token function">operation</span><span class="token punctuation">(</span>key<span class="token punctuation">,</span> accumulator <span class="token keyword">as</span> S<span class="token punctuation">,</span> element<span class="token punctuation">)</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
  <span class="token keyword">return</span> destination
<span class="token punctuation">}</span>
</code></pre>
<hr>
<p>Follow tôi, chúng tôi <a href="https://rx-mobile-team.github.io/profile/">https://rx-mobile-team.github.io/profile/</a> để có thêm nhiều kiến thức về lập trình, không chỉ giới hạn
ở Mobile (Android/iOS/Flutter) mà có cả Functional Programming, Reactive Programming, Data Structures, Algorithms, ...
Những kiến thức chia sẻ ở đây, rất ít các Senior Dev và vân..vân.. chia sẻ cho các bạn đâu.</p>]]></content:encoded>
            <author>hoc081098@gmail.com (Petrus Nguyễn Thái Học (hoc081098))</author>
        </item>
        <item>
            <title><![CDATA[Looking at `Dp` class in Jetpack Compose]]></title>
            <link>https://portfolio.hoc081098.dev/articles/jetpack-compose-dp-class</link>
            <guid>https://portfolio.hoc081098.dev/articles/jetpack-compose-dp-class</guid>
            <pubDate>Sat, 16 Sep 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>When using Jetpack Compose, we use <code class="notranslate" translate="no">Dp</code> type to represent a dimension value representing device-independent
pixels (dp).
It is used many times and everywhere in Jetpack Compose.</p>
<p>Do you have the same question as me 😇, when looking Jetpack Compose source code:
<strong>Why do @Composable functions in Jetpack Compose usually use <code class="notranslate" translate="no">Dp.Unspecified</code> as default value? Why not use null?</strong></p>
<p align="center"><img alt="Dp.Unspecified is usually used as default value in Jetpack Compose source code" loading="lazy" width="600" height="623" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fimg_2.25d30251.png&amp;w=640&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fimg_2.25d30251.png&amp;w=1200&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fimg_2.25d30251.png&amp;w=1200&amp;q=75"></p>
<p><em>Dp.Unspecified is usually used as default value in Jetpack Compose source code</em></p>
<h2>I. <code class="notranslate" translate="no">Dp</code> class</h2>
<h3>I.1. <code class="notranslate" translate="no">Dp</code> class declaration</h3>
<p>At the moment, <code class="notranslate" translate="no">Dp</code> class is defined as follows:</p>
<pre class="notranslate language-kotlin" translate="no"><code class="notranslate language-kotlin" translate="no"><span class="token annotation builtin">@Immutable</span>
<span class="token label symbol">@kotlin</span><span class="token punctuation">.</span>jvm<span class="token punctuation">.</span>JvmInline
value <span class="token keyword">class</span> <span class="token function">Dp</span><span class="token punctuation">(</span><span class="token keyword">val</span> value<span class="token operator">:</span> Float<span class="token punctuation">)</span> <span class="token operator">:</span> Comparable<span class="token operator">&lt;</span>Dp<span class="token operator">&gt;</span> <span class="token punctuation">{</span>
    <span class="token annotation builtin">@Stable</span>
    <span class="token keyword">inline</span> <span class="token keyword">operator</span> <span class="token keyword">fun</span> <span class="token function">plus</span><span class="token punctuation">(</span>other<span class="token operator">:</span> Dp<span class="token punctuation">)</span><span class="token operator">:</span> Dp <span class="token operator">=</span> <span class="token operator">..</span><span class="token punctuation">.</span>

    <span class="token annotation builtin">@Stable</span>
    <span class="token keyword">inline</span> <span class="token keyword">operator</span> <span class="token keyword">fun</span> <span class="token function">minus</span><span class="token punctuation">(</span>other<span class="token operator">:</span> Dp<span class="token punctuation">)</span><span class="token operator">:</span> Dp <span class="token operator">=</span> <span class="token operator">..</span><span class="token punctuation">.</span>

    <span class="token comment">// [...]</span>
    <span class="token comment">// Other operators ...</span>
    <span class="token comment">// [...]</span>

    <span class="token annotation builtin">@Stable</span>
    <span class="token keyword">override</span> <span class="token comment">/* TODO: inline */</span> <span class="token keyword">operator</span> <span class="token keyword">fun</span> <span class="token function">compareTo</span><span class="token punctuation">(</span>other<span class="token operator">:</span> Dp<span class="token punctuation">)</span> <span class="token operator">=</span> value<span class="token punctuation">.</span><span class="token function">compareTo</span><span class="token punctuation">(</span>other<span class="token punctuation">.</span>value<span class="token punctuation">)</span>

    <span class="token annotation builtin">@Stable</span>
    <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>isUnspecified<span class="token punctuation">)</span> <span class="token string-literal singleline"><span class="token string">"Dp.Unspecified"</span></span> <span class="token keyword">else</span> <span class="token string-literal singleline"><span class="token string">"</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$</span><span class="token expression">value</span></span><span class="token string">.dp"</span></span>

    <span class="token keyword">companion</span> <span class="token keyword">object</span> <span class="token punctuation">{</span>
        <span class="token annotation builtin">@Stable</span>
        <span class="token keyword">val</span> Hairline <span class="token operator">=</span> <span class="token function">Dp</span><span class="token punctuation">(</span>value <span class="token operator">=</span> <span class="token number">0f</span><span class="token punctuation">)</span>

        <span class="token annotation builtin">@Stable</span>
        <span class="token keyword">val</span> Infinity <span class="token operator">=</span> <span class="token function">Dp</span><span class="token punctuation">(</span>value <span class="token operator">=</span> Float<span class="token punctuation">.</span>POSITIVE_INFINITY<span class="token punctuation">)</span>

        <span class="token comment">/**
         * Constant that means unspecified Dp
         */</span>
        <span class="token annotation builtin">@Stable</span>
        <span class="token keyword">val</span> Unspecified <span class="token operator">=</span> <span class="token function">Dp</span><span class="token punctuation">(</span>value <span class="token operator">=</span> Float<span class="token punctuation">.</span>NaN<span class="token punctuation">)</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<p><code class="notranslate" translate="no">Dp</code> class is declared as <code class="notranslate" translate="no">inline value class</code> wrapping a <code class="notranslate" translate="no">Float</code> value.
When compiling, the compiler will try to replace all <code class="notranslate" translate="no">Dp</code> types with <code class="notranslate" translate="no">Float</code> types (<code class="notranslate" translate="no">float</code> in Java) as much as
possible.
This reduces memory usage and improves performance since heap allocation is eliminated,
and <code class="notranslate" translate="no">Float</code> type, a primitive type, is usually heavily optimized by the runtime.
That is the optimization of Jetpack Compose.</p>
<blockquote>
<p>The inline class spec is <a href="https://github.com/Kotlin/KEEP/blob/master/proposals/inline-classes.md">here</a>.</p>
</blockquote>
<h3>I.2. Example ✍️</h3>
<p>Let's create a simple example to see how the compiler optimizes <code class="notranslate" translate="no">Dp</code> class, I create a <code class="notranslate" translate="no">@Composable fun MyComposable1</code>
accepting a <code class="notranslate" translate="no">Dp</code> as the first parameter.</p>
<p align="center"><img alt="MyComposable1 accepts a Dp as the first parameter" loading="lazy" width="400" height="384" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fimg.e08a3bf7.png&amp;w=640&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fimg.e08a3bf7.png&amp;w=828&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fimg.e08a3bf7.png&amp;w=828&amp;q=75"></p>
<p><em>MyComposable1 accepts a Dp as the first parameter</em></p>
<br>
<p>After compiling Kotlin and decompiling Java bytecode to Java code,
we can see that the compiler has replaced <code class="notranslate" translate="no">Dp</code> types with <code class="notranslate" translate="no">Float</code> types.</p>
<p align="center"><img alt="Dp types are replaced with Float types" loading="lazy" width="600" height="258" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fimg_1.5ffaac11.png&amp;w=640&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fimg_1.5ffaac11.png&amp;w=1200&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fimg_1.5ffaac11.png&amp;w=1200&amp;q=75"></p>
<p><em>Dp types are replaced with Float types</em></p>
<br>
<p>You may notice that the method name <code class="notranslate" translate="no">MyComposable1</code> has been changed, that is
called <a href="https://kotlinlang.org/docs/inline-classes.html#mangling">mangling</a>.</p>
<blockquote>
<p>Mangling: since inline classes are compiled to their underlying type, it may lead to various obscure errors, for
example
unexpected platform signature clashes.
To mitigate such issues, functions using inline classes are mangled by adding some stable hashcode to the function
name.</p>
</blockquote>
<h2>II. Why do @Composable functions in Jetpack Compose usually use <code class="notranslate" translate="no">Dp.Unspecified</code> as default value? Why not use null?</h2>
<h3>II.1. Why not use null?</h3>
<p>After looking at the <code class="notranslate" translate="no">Dp</code> class, we can understand why <code class="notranslate" translate="no">Dp</code> is declared as <code class="notranslate" translate="no">inline value class</code>,
and the optimization of Jetpack Compose under the hood.
Let's get back to the question at the beginning of this article.</p>
<p align="center"><img alt="Dp.Unspecified is usually used as default value in Jetpack Compose source code" loading="lazy" width="600" height="623" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fimg_2.25d30251.png&amp;w=640&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fimg_2.25d30251.png&amp;w=1200&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fimg_2.25d30251.png&amp;w=1200&amp;q=75"></p>
<p><em>Dp.Unspecified is usually used as default value in Jetpack Compose source code</em></p>
<br>
<p><code class="notranslate" translate="no">Inline value class</code> and <code class="notranslate" translate="no">Auto Boxing</code> are keywords here.</p>
<blockquote>
<p>"Boxing" refers to converting a primitive value into a corresponding wrapper object. Because this can happen
automatically, it's known as autoboxing.
Similarly, when a wrapper object is unwrapped into a primitive value then this is known as unboxing.
(<a href="https://www.baeldung.com/java-wrapper-classes#autoboxing-and-unboxing">https://www.baeldung.com/java-wrapper-classes#autoboxing-and-unboxing</a>)</p>
</blockquote>
<p>If we use <code class="notranslate" translate="no">null</code> as default value, we must declare the type as <code class="notranslate" translate="no">Dp?</code> (nullable type).
Since <code class="notranslate" translate="no">Dp?</code> is a nullable type, compiler cannot use <code class="notranslate" translate="no">Float</code> (aka <code class="notranslate" translate="no">float</code> in Java) type to replace <code class="notranslate" translate="no">Dp?</code> type.
Instead, <code class="notranslate" translate="no">Dp?</code> must be used!!!</p>
<blockquote>
<p>nullable inline class types over primitive types are mapped to the boxed reference type (wrapper of an inline class)</p>
</blockquote>
<p><code class="notranslate" translate="no">Dp?</code> is a reference type, not a primitive type, it requires <strong>heap allocation</strong>.
Imagine we use <code class="notranslate" translate="no">Dp?</code> types in many places (the worst case is in <strong>a loop</strong> or is in <strong>List/Row/Column/... UI</strong>),
it will cause a lot of heap allocation, affect the application performance ⚠️,
especially a UI application, its performance is very important to the user experience.</p>
<h3>II.2. Demonstration example ✍️</h3>
<p>Let's create a simple example using <code class="notranslate" translate="no">Dp?</code> type, we create a <code class="notranslate" translate="no">@Composable fun MyComposable2</code> accepting a <code class="notranslate" translate="no">Dp?</code> as the
first parameter.</p>
<p align="center"><img alt="MyComposable2 accepts a Dp? as the first parameter" loading="lazy" width="400" height="386" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fimg_3.68d6f53f.png&amp;w=640&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fimg_3.68d6f53f.png&amp;w=828&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fimg_3.68d6f53f.png&amp;w=828&amp;q=75"></p>
<p><em>MyComposable2 accepts a Dp? as the first parameter</em></p>
<br>
<p>After decompiling, we can see that <code class="notranslate" translate="no">Dp</code> type is not replaced with <code class="notranslate" translate="no">Float</code> type, it is preserved.
Inside <code class="notranslate" translate="no">UseMyComposable2</code>, <code class="notranslate" translate="no">Dp.box-impl(Dp.constructor-impl((float)$this$dp$iv))</code> is used
to box a <code class="notranslate" translate="no">Float</code> value to a <code class="notranslate" translate="no">Dp</code>.
It is a heap allocation ⚠️.</p>
<p align="center"><img alt="Dp type is not replaced with Float type and heap allocation is used" loading="lazy" width="600" height="254" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fimg_4.2f8de7e7.png&amp;w=640&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fimg_4.2f8de7e7.png&amp;w=1200&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fimg_4.2f8de7e7.png&amp;w=1200&amp;q=75"></p>
<p><em>Dp type is not replaced with Float type and heap allocation is used</em></p>
<br>
<p><strong>That is the reason why we must not use <code class="notranslate" translate="no">null</code> as default value</strong>.
<code class="notranslate" translate="no">Dp.Unspecified</code> is a better choice ✅, it wraps a <code class="notranslate" translate="no">Float.NaN</code> value.
Sometimes, we can use <code class="notranslate" translate="no">Dp.Hairline</code> (aka <code class="notranslate" translate="no">0.dp</code>) as default value, depending on your use case.</p>
<h3>II.3. Bonus ✅</h3>
<ul>
<li>How about elvis operator <code class="notranslate" translate="no">?:</code>?</li>
<li>How about <code class="notranslate" translate="no">== null</code>?</li>
</ul>
<p>Jetpack Compose has replacements, they are <code class="notranslate" translate="no">Dp.takeOrElse(block: () -&gt; Dp): Dp</code> and <code class="notranslate" translate="no">Dp.isUnspecified: Boolean</code>.
<code class="notranslate" translate="no">takeOrElse</code> is an inline function, no lambda allocation, no heap allocation here 🥰.</p>
<p align="center"><img alt="Dp.takeOrElse and Dp.isUnspecified" loading="lazy" width="600" height="260" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fimg_5.cc4f4093.png&amp;w=640&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fimg_5.cc4f4093.png&amp;w=1200&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fimg_5.cc4f4093.png&amp;w=1200&amp;q=75"></p>
<p><em>Dp.takeOrElse and Dp.isUnspecified</em></p>
<hr>
<p>Thanks for reading 🤗.</p>
<p>Follow me (@hoc081098) and us (<a href="https://rx-mobile-team.github.io/profile/">https://rx-mobile-team.github.io/profile/</a>) to have more knowledge about programming, not only limited to
Mobile (Android/iOS/Flutter) but also Functional Programming, Reactive Programming, Data Structures, Algorithms, ...</p>]]></content:encoded>
            <author>hoc081098@gmail.com (Petrus Nguyễn Thái Học (hoc081098))</author>
        </item>
        <item>
            <title><![CDATA[Kleisli: Function Composition for Effectful Functions]]></title>
            <link>https://portfolio.hoc081098.dev/articles/kleisli-note</link>
            <guid>https://portfolio.hoc081098.dev/articles/kleisli-note</guid>
            <pubDate>Tue, 19 May 2026 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h2>1. 💠 Kleisli có thể được hiểu như một Arrow (Function) trả về một Effect</h2>
<p>Thay vì một function thuần: <code class="notranslate" translate="no">A -&gt; C</code>, Kleisli biểu diễn một function có context/effect: <code class="notranslate" translate="no">A -&gt; M&lt;C&gt;</code>.</p>
<p>Ví dụ với Continuation: <code class="notranslate" translate="no">Kleisli&lt;Cont&lt;R, *&gt;, A, C&gt;</code> ≅ <code class="notranslate" translate="no">A -&gt; Cont&lt;R, C&gt;</code> trong đó <code class="notranslate" translate="no">Cont&lt;R, A&gt;</code> ≅ <code class="notranslate" translate="no">(A -&gt; R) -&gt; R</code>.</p>
<p>Nói tổng quát hơn thì <code class="notranslate" translate="no">Kleisli&lt;M, A, B&gt;</code> ≅ <code class="notranslate" translate="no">A -&gt; M&lt;B&gt;</code>.
Trong đó <code class="notranslate" translate="no">M</code> là một <strong>type constructor</strong> đại diện cho effect/context,
chẳng hạn như <code class="notranslate" translate="no">Option</code>, <code class="notranslate" translate="no">Either&lt;E, *&gt;</code>, <code class="notranslate" translate="no">IO</code>, <code class="notranslate" translate="no">Cont&lt;R, *&gt;</code>, etc.</p>
<h2>2. 💠 Nếu M là một Monad, thì operation quan trọng nhất của Kleisli là <code class="notranslate" translate="no">andThen</code></h2>
<p><code class="notranslate" translate="no">andThen</code> giúp compose các effectful arrows với nhau.
Ở đây <code class="notranslate" translate="no">Kleisli&lt;M, A, B&gt;</code> chỉ là notation/pseudocode để biểu diễn ý tưởng <code class="notranslate" translate="no">A -&gt; M&lt;B&gt;</code>.
Trong các ngôn ngữ có HKT (Higher-Kinded Types) như Scala, ta có thể viết gần đúng là <code class="notranslate" translate="no">Kleisli[F[_], A, B]</code>.
Kotlin không có HKT thật, nên nếu implement thực tế cần dùng encoding riêng, wrapper riêng.</p>
<h3>Signature</h3>
<pre class="notranslate language-haskell" translate="no"><code class="notranslate language-haskell" translate="no"><span class="token hvariable">andThen</span><span class="token operator">:</span> <span class="token constant">Kleisli</span><span class="token operator">&lt;</span><span class="token constant">M</span><span class="token punctuation">,</span> <span class="token constant">A</span><span class="token punctuation">,</span> <span class="token constant">B</span><span class="token operator">&gt;</span> <span class="token operator">-&gt;</span> <span class="token punctuation">(</span><span class="token constant">Kleisli</span><span class="token operator">&lt;</span><span class="token constant">M</span><span class="token punctuation">,</span> <span class="token constant">B</span><span class="token punctuation">,</span> <span class="token constant">C</span><span class="token operator">&gt;</span><span class="token punctuation">)</span> <span class="token operator">-&gt;</span> <span class="token constant">Kleisli</span><span class="token operator">&lt;</span><span class="token constant">M</span><span class="token punctuation">,</span> <span class="token constant">A</span><span class="token punctuation">,</span> <span class="token constant">C</span><span class="token operator">&gt;</span>
</code></pre>
<p>có thể hiểu là:</p>
<pre class="notranslate language-haskell" translate="no"><code class="notranslate language-haskell" translate="no"><span class="token hvariable">andThen</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token constant">A</span> <span class="token operator">-&gt;</span> <span class="token constant">M</span><span class="token operator">&lt;</span><span class="token constant">B</span><span class="token operator">&gt;</span><span class="token punctuation">)</span> <span class="token operator">-&gt;</span> <span class="token punctuation">(</span><span class="token constant">B</span> <span class="token operator">-&gt;</span> <span class="token constant">M</span><span class="token operator">&lt;</span><span class="token constant">C</span><span class="token operator">&gt;</span><span class="token punctuation">)</span> <span class="token operator">-&gt;</span> <span class="token punctuation">(</span><span class="token constant">A</span> <span class="token operator">-&gt;</span> <span class="token constant">M</span><span class="token operator">&lt;</span><span class="token constant">C</span><span class="token operator">&gt;</span><span class="token punctuation">)</span>
</code></pre>
<h3>Ví dụ bằng Kotlin-like pseudocode</h3>
<pre class="notranslate language-kotlin" translate="no"><code class="notranslate language-kotlin" translate="no"><span class="token comment">// Kotlin-like pseudocode</span>
Kleisli<span class="token operator">&lt;</span>M<span class="token punctuation">,</span> A<span class="token punctuation">,</span> B<span class="token operator">&gt;</span><span class="token punctuation">.</span><span class="token function">andThen</span><span class="token punctuation">(</span>
    f<span class="token operator">:</span> Kleisli<span class="token operator">&lt;</span>M<span class="token punctuation">,</span> B<span class="token punctuation">,</span> C<span class="token operator">&gt;</span>
<span class="token punctuation">)</span><span class="token operator">:</span> Kleisli<span class="token operator">&lt;</span>M<span class="token punctuation">,</span> A<span class="token punctuation">,</span> C<span class="token operator">&gt;</span>
</code></pre>
<p>Có thể được triển khai như sau:</p>
<pre class="notranslate language-kotlin" translate="no"><code class="notranslate language-kotlin" translate="no"><span class="token comment">// Kotlin-like pseudocode</span>
<span class="token keyword">fun</span> Kleisli<span class="token operator">&lt;</span>M<span class="token punctuation">,</span> A<span class="token punctuation">,</span> B<span class="token operator">&gt;</span><span class="token punctuation">.</span><span class="token function">andThen</span><span class="token punctuation">(</span>
    f<span class="token operator">:</span> Kleisli<span class="token operator">&lt;</span>M<span class="token punctuation">,</span> B<span class="token punctuation">,</span> C<span class="token operator">&gt;</span>
<span class="token punctuation">)</span><span class="token operator">:</span> Kleisli<span class="token operator">&lt;</span>M<span class="token punctuation">,</span> A<span class="token punctuation">,</span> C<span class="token operator">&gt;</span> <span class="token operator">=</span>
    Kleisli <span class="token punctuation">{</span> a <span class="token operator">-&gt;</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">run</span><span class="token punctuation">(</span>a<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">flatMap</span> <span class="token punctuation">{</span> b <span class="token operator">-&gt;</span> f<span class="token punctuation">.</span><span class="token function">run</span><span class="token punctuation">(</span>b<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span>
</code></pre>
<p>Điểm mấu chốt nằm ở <code class="notranslate" translate="no">flatMap</code>. Vì <code class="notranslate" translate="no">this.run(a)</code> trả về một <code class="notranslate" translate="no">M&lt;B&gt;</code>, nên ta <strong>không có ngay B</strong> để truyền tiếp vào <code class="notranslate" translate="no">f</code>.
Chính <code class="notranslate" translate="no">flatMap</code> cho phép ta truyền một function <code class="notranslate" translate="no">B -&gt; M&lt;C&gt;</code> vào bên trong context <code class="notranslate" translate="no">M</code>,
để chain computation hiện tại <code class="notranslate" translate="no">M&lt;B&gt;</code> với computation tiếp theo <code class="notranslate" translate="no">M&lt;C&gt;</code>
mà không cần unwrap <code class="notranslate" translate="no">B</code> ra khỏi context một cách thủ công (và thường là không thể).</p>
<h2>3. 💠 Trong Haskell, đây chính là Kleisli composition</h2>
<pre class="notranslate language-haskell" translate="no"><code class="notranslate language-haskell" translate="no"><span class="token punctuation">(</span><span class="token operator">&gt;=&gt;</span><span class="token punctuation">)</span> <span class="token operator">::</span> <span class="token constant">Monad</span> <span class="token hvariable">m</span> <span class="token operator">=&gt;</span> <span class="token punctuation">(</span><span class="token hvariable">a</span> <span class="token operator">-&gt;</span> <span class="token hvariable">m</span> <span class="token hvariable">b</span><span class="token punctuation">)</span> <span class="token operator">-&gt;</span> <span class="token punctuation">(</span><span class="token hvariable">b</span> <span class="token operator">-&gt;</span> <span class="token hvariable">m</span> <span class="token hvariable">c</span><span class="token punctuation">)</span> <span class="token operator">-&gt;</span> <span class="token punctuation">(</span><span class="token hvariable">a</span> <span class="token operator">-&gt;</span> <span class="token hvariable">m</span> <span class="token hvariable">c</span><span class="token punctuation">)</span>
<span class="token hvariable">f</span> <span class="token operator">&gt;=&gt;</span> <span class="token hvariable">g</span> <span class="token operator">=</span> <span class="token operator">\</span><span class="token hvariable">a</span> <span class="token operator">-&gt;</span> <span class="token hvariable">f</span> <span class="token hvariable">a</span> <span class="token operator">&gt;&gt;=</span> <span class="token hvariable">g</span>
</code></pre>
<ul>
<li><code class="notranslate" translate="no">&gt;&gt;=</code> là <code class="notranslate" translate="no">bind</code> operator của Monad, hay còn gọi là <code class="notranslate" translate="no">flatMap</code> trong các ngôn ngữ khác.</li>
<li><code class="notranslate" translate="no">\a -&gt; f a &gt;&gt;= g</code> là lambda expression với tham số <code class="notranslate" translate="no">a</code> và biểu thức trả về là <code class="notranslate" translate="no">f a &gt;&gt;= g</code>,
nó tương đương với <code class="notranslate" translate="no">f(a).flatMap(g)</code> trong các ngôn ngữ khác.</li>
</ul>
<p>Vì <code class="notranslate" translate="no">&gt;=&gt;</code> có hình dạng khá giống một con cá, nên các developers hay gọi vui <code class="notranslate" translate="no">&gt;=&gt;</code> là Right Fish operator 🐟.</p>
<h2>4. Kết lại</h2>
<h3>Function composition thông thường là: <code class="notranslate" translate="no">(.) :: (b -&gt; c) -&gt; (a -&gt; b) -&gt; (a -&gt; c)</code></h3>
<p>Chú ý thứ tự invoke functions từ phải sang trái.</p>
<p>Ví dụ: <code class="notranslate" translate="no">g . f</code> bằng với <code class="notranslate" translate="no">\x -&gt; g (f x)</code>, tức là chạy <code class="notranslate" translate="no">f</code> trước rồi mới tới <code class="notranslate" translate="no">g</code>.</p>
<h3>Kleisli composition là phiên bản dành cho effectful functions: <code class="notranslate" translate="no">(&gt;=&gt;) :: (a -&gt; m b) -&gt; (b -&gt; m c) -&gt; (a -&gt; m c)</code></h3>
<p>Chú ý thứ tự invoke functions từ trái sang phải, giống như cách chúng ta đọc.</p>
<p>Ví dụ: <code class="notranslate" translate="no">f &gt;=&gt; g</code> bằng với <code class="notranslate" translate="no">\x -&gt; f x &gt;&gt;= g</code>, tức là chạy <code class="notranslate" translate="no">f</code> trước, rồi dùng <code class="notranslate" translate="no">bind</code>/<code class="notranslate" translate="no">flatMap</code> để chạy tiếp <code class="notranslate" translate="no">g</code>.</p>]]></content:encoded>
            <author>hoc081098@gmail.com (Petrus Nguyễn Thái Học (hoc081098))</author>
        </item>
        <item>
            <title><![CDATA[Under the Hood of Kotlin ArrayDeque: Circular Buffer, Capacity Growth, and Complexity Analysis]]></title>
            <link>https://portfolio.hoc081098.dev/articles/kotlin-arraydeque-under-the-hood</link>
            <guid>https://portfolio.hoc081098.dev/articles/kotlin-arraydeque-under-the-hood</guid>
            <pubDate>Mon, 08 Jun 2026 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h2>Overview</h2>
<p>Ngay tại thời điểm này, khi AI Agents đang được sử dụng rộng rãi trong Software Development, mình có cảm giác ngày càng ít người hứng thú với những thứ nằm phía sau framework, library hay API mà họ sử dụng hằng ngày. Nhiều người chỉ quan tâm tới việc ship feature nhanh nhất có thể, còn data structures, algorithms hay internal implementation dần bị xem là những chủ đề “không còn quá cần thiết nữa”.</p>
<p>Nhưng với mình thì ngược lại. Mình vẫn dùng AI mỗi ngày để nghiên cứu, review và hỗ trợ viết code, nhưng càng như vậy mình càng thấy hứng thú với việc đọc source code, tìm hiểu implementation và hiểu rõ trade-offs đằng sau những abstraction mà chúng ta sử dụng, và phần nào giúp cho cái công việc gen/review code của Agent bớt nhàm chán hơn.</p>
<p>Tạm nói qua bối cảnh như vậy, hôm trước mình dùng Agent để gen code thì nó gen ra một đoạn code dùng Kotlin <code class="notranslate" translate="no">ArrayDeque</code>, mình đọc vào tất nhiên sẽ hiểu logic và semantics của nó rồi,
vì mình đã xài <code class="notranslate" translate="no">ArrayDeque</code> từ thời Java,
và bắt đầu xài Kotlin <code class="notranslate" translate="no">ArrayDeque</code> ngay từ khi nó được giới thiệu trong Kotlin 1.4.</p>
<p>Mình vẫn biết nó là “double-ended queue implementation backed by a resizable circular buffer”,
tức là một hàng đợi hai đầu, cho phép thêm/xóa ở cả đầu trước và đầu sau.
Nếu nhìn dưới góc độ amortized analysis, các thao tác thêm/xóa ở hai đầu là O(1).
Nó có thể được dùng như là <code class="notranslate" translate="no">Stack</code> (<code class="notranslate" translate="no">addLast</code>/<code class="notranslate" translate="no">removeLast</code>) hoặc <code class="notranslate" translate="no">Queue</code> (<code class="notranslate" translate="no">addLast</code>/<code class="notranslate" translate="no">removeFirst</code>) tùy vào mục đích sử dụng.</p>
<p>Ngày xưa, mình hay dùng nó khi cần duyệt graph, ví dụ <code class="notranslate" translate="no">BFS</code> với queue semantics (<code class="notranslate" translate="no">addLast</code>/<code class="notranslate" translate="no">removeFirst</code>) hoặc <code class="notranslate" translate="no">DFS</code> với stack semantics (<code class="notranslate" translate="no">addLast</code>/<code class="notranslate" translate="no">removeLast</code>).
Các bạn có thể xem PR <a href="https://github.com/InsertKoinIO/koin/pull/1801">https://github.com/InsertKoinIO/koin/pull/1801</a> - PR mình contribute vô <code class="notranslate" translate="no">Koin</code> repository để refactor một hàm đệ quy sang <code class="notranslate" translate="no">DFS</code>, hàm này flatten các <code class="notranslate" translate="no">Koin</code> modules.
Nhưng trước giờ mình cũng chỉ nhớ mang máng nó là <em>resizable circular buffer</em> chứ cũng không tìm hiểu rõ internal implementation của nó như nào. Hôm trước được dịp Agent gen code xong, thì mình cũng tranh thủ vô đọc Kotlin <code class="notranslate" translate="no">ArrayDeque</code> luôn cho biết.
Nên hôm nay, mình sẽ chia sẻ lại những gì mình đã tìm hiểu được về <code class="notranslate" translate="no">ArrayDeque</code> implementation, hy vọng sẽ giúp các bạn có thêm kiến thức về một data structure rất hay và hữu ích này.</p>
<hr>
<h2>I. Internal state</h2>
<pre class="notranslate language-kotlin" translate="no"><code class="notranslate language-kotlin" translate="no"><span class="token annotation builtin">@SinceKotlin</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"1.4"</span></span><span class="token punctuation">)</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> ArrayDeque<span class="token operator">&lt;</span>E<span class="token operator">&gt;</span> <span class="token operator">:</span> AbstractMutableList<span class="token operator">&lt;</span>E<span class="token operator">&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">private</span> <span class="token keyword">var</span> head<span class="token operator">:</span> Int <span class="token operator">=</span> <span class="token number">0</span>
  <span class="token keyword">private</span> <span class="token keyword">var</span> elementData<span class="token operator">:</span> Array<span class="token operator">&lt;</span>Any<span class="token operator">?</span><span class="token operator">&gt;</span>

  <span class="token keyword">override</span> <span class="token keyword">var</span> size<span class="token operator">:</span> Int <span class="token operator">=</span> <span class="token number">0</span>
    <span class="token keyword">private</span> <span class="token keyword">set</span>
<span class="token punctuation">}</span>
</code></pre>
<p>State cốt lõi của nó thực sự chỉ gồm:</p>
<ul>
<li><code class="notranslate" translate="no">elementData: Array&lt;Any?&gt;</code>: growable, nghĩa là khi cần thêm capacity thì nó sẽ được mở rộng lên. Đây chính là nơi lưu trữ data thực sự của <code class="notranslate" translate="no">ArrayDeque</code>.</li>
<li><code class="notranslate" translate="no">head</code>: index thực sự của phần tử head, nó là <em>physical index</em> trong <code class="notranslate" translate="no">elementData</code> chứ không phải <em>logical index</em> (logical index của head luôn là 0).</li>
<li><code class="notranslate" translate="no">size</code>: số phần tử hiện có trong <code class="notranslate" translate="no">ArrayDeque</code>.</li>
</ul>
<h3>I.1. Physical vs Logical index</h3>
<p>Trong <code class="notranslate" translate="no">ArrayDeque</code>, có 2 khái niệm về index:</p>
<ul>
<li>
<p><strong>Physical index</strong>: index thực sự trong <code class="notranslate" translate="no">elementData</code> array. Ví dụ là <code class="notranslate" translate="no">head</code>, nó có thể được dùng để truy cập phần tử head thực sự, cơ bản là <code class="notranslate" translate="no">elementData[head]</code>.</p>
</li>
<li>
<p><strong>Logical index</strong>: index mà phía bên ngoài nhìn vào. Đây là cái index mà developer chúng ta thường dùng để access element trong ArrayDeque.
Ví dụ <code class="notranslate" translate="no">ArrayDeque.get(0)</code> sẽ trả về phần tử head, <code class="notranslate" translate="no">ArrayDeque.get(size - 1)</code> sẽ trả về phần tử cuối cùng.</p>
</li>
</ul>
<h3>I.2. Derived tail</h3>
<p>Về mặt khái niệm, <code class="notranslate" translate="no">tail</code> là physical index ngay sau phần tử cuối cùng (exclusive).
Và <code class="notranslate" translate="no">tail</code> có thể được derived dễ dàng từ <code class="notranslate" translate="no">head</code> và <code class="notranslate" translate="no">size</code>:</p>
<pre class="notranslate language-kotlin" translate="no"><code class="notranslate language-kotlin" translate="no"><span class="token keyword">private</span> <span class="token keyword">fun</span> <span class="token function">positiveMod</span><span class="token punctuation">(</span>index<span class="token operator">:</span> Int<span class="token punctuation">)</span><span class="token operator">:</span> Int <span class="token operator">=</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>index <span class="token operator">&gt;=</span> elementData<span class="token punctuation">.</span>size<span class="token punctuation">)</span> index <span class="token operator">-</span> elementData<span class="token punctuation">.</span>size <span class="token keyword">else</span> index

<span class="token label symbol">@kotlin</span><span class="token punctuation">.</span>internal<span class="token punctuation">.</span>InlineOnly
<span class="token keyword">private</span> <span class="token keyword">inline</span> <span class="token keyword">fun</span> <span class="token function">internalIndex</span><span class="token punctuation">(</span>index<span class="token operator">:</span> Int<span class="token punctuation">)</span><span class="token operator">:</span> Int <span class="token operator">=</span> <span class="token function">positiveMod</span><span class="token punctuation">(</span>head <span class="token operator">+</span> index<span class="token punctuation">)</span>

<span class="token keyword">val</span> tail <span class="token operator">=</span> <span class="token function">internalIndex</span><span class="token punctuation">(</span>size<span class="token punctuation">)</span>
</code></pre>
<p><code class="notranslate" translate="no">internalIndex</code> là method để convert <em>logical index</em> (0-based) sang <em>physical index</em> trong <code class="notranslate" translate="no">elementData</code>.
Logical index của head luôn là 0, tail thì có logical index là <code class="notranslate" translate="no">size</code>.
<code class="notranslate" translate="no">positiveMod</code> là method để wrap-around qua 0 khi index vượt quá capacity của <code class="notranslate" translate="no">elementData</code>.</p>
<p>Về mặt khái niệm có thể hiểu gần như modulo,
nhưng implementation thực tế chỉ cần subtract một lần vì input đã nằm trong khoảng có thể wrap tối đa một vòng.</p>
<pre class="notranslate" translate="no"><code class="notranslate" translate="no">positiveMod(x) = x mod elementData.size
tail = (head + size) mod elementData.size
</code></pre>
<p>Tại sao không lưu <code class="notranslate" translate="no">tail</code> như một field riêng biệt mà lại derived như vậy?
Vì <code class="notranslate" translate="no">tail</code> được derived như vậy để tiết kiệm 1 field, đồng thời đảm bảo <strong>invariant</strong> “tail = head + size” không thể bị drift (vì nó luôn được derived từ <code class="notranslate" translate="no">head</code> và <code class="notranslate" translate="no">size</code>).</p>
<h3>I.3. Internal layout</h3>
<p>Trong phần lớn thời gian, ArrayDeque sẽ ở một trong hai trạng thái:</p>
<ul>
<li><code class="notranslate" translate="no">head</code> nhỏ hơn <code class="notranslate" translate="no">tail</code> (contiguous): các phần tử nằm liên tiếp trong <code class="notranslate" translate="no">elementData</code> từ <code class="notranslate" translate="no">head</code> đến <code class="notranslate" translate="no">tail-1</code>.</li>
</ul>
<pre class="notranslate" translate="no"><code class="notranslate" translate="no">[##, ##, e1, e2, e3, ##]
         ↑           ↑
        head        tail
</code></pre>
<ul>
<li><code class="notranslate" translate="no">head</code> lớn hơn <code class="notranslate" translate="no">tail</code> (wrapped): sau khi <code class="notranslate" translate="no">tail</code> vượt quá cuối array và vòng về 0, các phần tử sẽ nằm ở 2 đầu của <code class="notranslate" translate="no">elementData</code>.</li>
</ul>
<pre class="notranslate" translate="no"><code class="notranslate" translate="no">[e3, ##, ##, ##, e1, e2]
     ↑           ↑
    tail       head
</code></pre>
<p>Với 2 ví dụ trên thì nếu nhìn từ bên ngoài vào thì cả 2 đều có data giống nhau <code class="notranslate" translate="no">[e1, e2, e3]</code>.</p>
<hr>
<h2>II. Operations</h2>
<p>Ta thử tìm hiểu 4 methods chính của <code class="notranslate" translate="no">ArrayDeque</code>: <code class="notranslate" translate="no">addLast</code>, <code class="notranslate" translate="no">addFirst</code>, <code class="notranslate" translate="no">removeFirst</code>, <code class="notranslate" translate="no">removeLast</code>
để xem <code class="notranslate" translate="no">head</code>, <code class="notranslate" translate="no">tail</code> và backing array thay đổi như thế nào khi add/remove ở hai đầu.</p>
<h3>II.1. addLast</h3>
<pre class="notranslate language-kotlin" translate="no"><code class="notranslate language-kotlin" translate="no"><span class="token keyword">class</span> ArrayDeque<span class="token operator">&lt;</span>E<span class="token operator">&gt;</span> <span class="token operator">:</span> AbstractMutableList<span class="token operator">&lt;</span>E<span class="token operator">&gt;</span> <span class="token punctuation">{</span>
  <span class="token comment">/**
   * Appends the specified [element] to this deque.
   */</span>
  <span class="token keyword">public</span> <span class="token keyword">fun</span> <span class="token function">addLast</span><span class="token punctuation">(</span>element<span class="token operator">:</span> E<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token function">registerModification</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token function">ensureCapacity</span><span class="token punctuation">(</span>size <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span>

    elementData<span class="token punctuation">[</span><span class="token function">internalIndex</span><span class="token punctuation">(</span>size<span class="token punctuation">)</span><span class="token punctuation">]</span> <span class="token operator">=</span> element
    size <span class="token operator">+=</span> <span class="token number">1</span>
  <span class="token punctuation">}</span>

  <span class="token comment">/**
   * Ensures that the capacity of this deque is at least equal to the specified [minCapacity].
   *
   * If the current capacity is less than the [minCapacity], a new backing storage is allocated with greater capacity.
   * Otherwise, this method takes no action and simply returns.
   */</span>
  <span class="token keyword">private</span> <span class="token keyword">fun</span> <span class="token function">ensureCapacity</span><span class="token punctuation">(</span>minCapacity<span class="token operator">:</span> Int<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token operator">..</span><span class="token punctuation">.</span> <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<ul>
<li>
<p>Đầu tiên, nó sẽ đảm bảo <code class="notranslate" translate="no">elementData.size &gt;= size + 1</code> bằng cách gọi <code class="notranslate" translate="no">ensureCapacity</code>,
nghĩa là <code class="notranslate" translate="no">elementData</code> có đủ chỗ trống để thêm 1 phần tử vào đuôi.
Nếu capacity hiện tại không đủ thì sẽ được mở rộng lên (ta sẽ tìm hiểu việc mở rộng này ở phần sau).</p>
</li>
<li>
<p><code class="notranslate" translate="no">internalIndex(size)</code> để lấy <em>physical index</em> của <code class="notranslate" translate="no">size</code> (size chính là <em>logical index</em> của tail), sau đó gán <code class="notranslate" translate="no">element</code> vào đó.</p>
</li>
<li>
<p>Sau cùng tăng <code class="notranslate" translate="no">size</code> lên 1, lúc này chỗ nào dùng <code class="notranslate" translate="no">tail</code> thì cũng sẽ reflect value mới, vì <code class="notranslate" translate="no">tail</code> được derived từ <code class="notranslate" translate="no">size</code> và <code class="notranslate" translate="no">head</code>.</p>
</li>
</ul>
<p>Chỗ đặc biệt là <code class="notranslate" translate="no">internalIndex</code> nó có cơ chế wrap-around qua 0 khi index vượt quá capacity của <code class="notranslate" translate="no">elementData</code>, nên khi <code class="notranslate" translate="no">tail</code> vượt quá cuối array thì nó sẽ tự động vòng về 0 để tiếp tục add phần tử vào đầu array.
Đây chính là circular buffer semantics. Lấy ví dụ:</p>
<pre class="notranslate" translate="no"><code class="notranslate" translate="no">[##, ##, e1, e2, e3]
         ↑
        head
# size = 3, head = 2, tail = 0, capacity = 5

-------- addLast(e4) --------

[e4, ##, e1, e2, e3]
     ↑   ↑
    tail head
# size = 4, head = 2, tail = 1, capacity = 5
</code></pre>
<h3>II.2. addFirst</h3>
<pre class="notranslate language-kotlin" translate="no"><code class="notranslate language-kotlin" translate="no"><span class="token keyword">class</span> ArrayDeque<span class="token operator">&lt;</span>E<span class="token operator">&gt;</span> <span class="token operator">:</span> AbstractMutableList<span class="token operator">&lt;</span>E<span class="token operator">&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">private</span> <span class="token keyword">fun</span> <span class="token function">decremented</span><span class="token punctuation">(</span>index<span class="token operator">:</span> Int<span class="token punctuation">)</span><span class="token operator">:</span> Int <span class="token operator">=</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>index <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span> elementData<span class="token punctuation">.</span>lastIndex <span class="token keyword">else</span> index <span class="token operator">-</span> <span class="token number">1</span>

  <span class="token comment">/**
   * Prepends the specified [element] to this deque.
   */</span>
  <span class="token keyword">public</span> <span class="token keyword">fun</span> <span class="token function">addFirst</span><span class="token punctuation">(</span>element<span class="token operator">:</span> E<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token function">registerModification</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token function">ensureCapacity</span><span class="token punctuation">(</span>size <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span>

    head <span class="token operator">=</span> <span class="token function">decremented</span><span class="token punctuation">(</span>head<span class="token punctuation">)</span>
    elementData<span class="token punctuation">[</span>head<span class="token punctuation">]</span> <span class="token operator">=</span> element
    size <span class="token operator">+=</span> <span class="token number">1</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<ul>
<li>
<p>Tương tự như <code class="notranslate" translate="no">addLast</code>, nó cũng sẽ đảm bảo capacity đủ để thêm phần tử mới.</p>
</li>
<li>
<p>Cái hay ở chỗ là <code class="notranslate" translate="no">head</code> bây giờ phải lùi về trước, thông qua <code class="notranslate" translate="no">head = decremented(head)</code>. Ta có thể hiểu theo kiểu logical index là <code class="notranslate" translate="no">head = head-1</code>, nhưng vì <code class="notranslate" translate="no">head</code> là physical index nên nó phải được <code class="notranslate" translate="no">decrement</code> với wrap-around qua 0, nghĩa là nếu <code class="notranslate" translate="no">head</code> đang ở 0 thì nó sẽ lùi về cuối array (<code class="notranslate" translate="no">elementData.lastIndex</code>). Ta thấy nó đi theo chiều ngược lại so với <code class="notranslate" translate="no">addLast</code>, nhưng vẫn đảm bảo được circular buffer semantics.</p>
</li>
<li>
<p>Gán <code class="notranslate" translate="no">element</code> vào <code class="notranslate" translate="no">elementData[head]</code> sau khi đã lùi <code class="notranslate" translate="no">head</code> về trước, rồi tăng <code class="notranslate" translate="no">size</code> lên 1.</p>
</li>
</ul>
<p>Lấy ví dụ circular buffer semantics, khi head đã ở vị trí 0 và ta <code class="notranslate" translate="no">addFirst</code> thêm 1 phần tử nữa thì head sẽ lùi về cuối array:</p>
<pre class="notranslate" translate="no"><code class="notranslate" translate="no">[e1, e2, e3, ##, ##]
 ↑
head
# size = 3, head = 0, tail = 3, capacity = 5

-------- addFirst(e0) --------

[e1, e2, e3, ##, e0]
             ↑   ↑
            tail head
# size = 4, head = 4, tail = 3, capacity = 5
</code></pre>
<h3>II.3. removeFirst</h3>
<pre class="notranslate language-kotlin" translate="no"><code class="notranslate language-kotlin" translate="no"><span class="token keyword">class</span> ArrayDeque<span class="token operator">&lt;</span>E<span class="token operator">&gt;</span> <span class="token operator">:</span> AbstractMutableList<span class="token operator">&lt;</span>E<span class="token operator">&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">private</span> <span class="token keyword">fun</span> <span class="token function">incremented</span><span class="token punctuation">(</span>index<span class="token operator">:</span> Int<span class="token punctuation">)</span><span class="token operator">:</span> Int <span class="token operator">=</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>index <span class="token operator">==</span> elementData<span class="token punctuation">.</span>lastIndex<span class="token punctuation">)</span> <span class="token number">0</span> <span class="token keyword">else</span> index <span class="token operator">+</span> <span class="token number">1</span>

  <span class="token comment">/**
   * Removes the first element from this deque and returns that removed element, or throws [NoSuchElementException] if this deque is empty.
   */</span>
  <span class="token annotation builtin">@IgnorableReturnValue</span>
  <span class="token keyword">public</span> <span class="token keyword">fun</span> <span class="token function">removeFirst</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> E <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">isEmpty</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">throw</span> <span class="token function">NoSuchElementException</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"ArrayDeque is empty."</span></span><span class="token punctuation">)</span>
    <span class="token function">registerModification</span><span class="token punctuation">(</span><span class="token punctuation">)</span>

    <span class="token keyword">val</span> element <span class="token operator">=</span> <span class="token function">internalGet</span><span class="token punctuation">(</span>head<span class="token punctuation">)</span>
    elementData<span class="token punctuation">[</span>head<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token keyword">null</span>
    head <span class="token operator">=</span> <span class="token function">incremented</span><span class="token punctuation">(</span>head<span class="token punctuation">)</span>
    size <span class="token operator">-=</span> <span class="token number">1</span>
    <span class="token keyword">return</span> element
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<ul>
<li>
<p>Đầu tiên, nó sẽ check nếu <code class="notranslate" translate="no">ArrayDeque</code> đang rỗng thì sẽ throw <code class="notranslate" translate="no">NoSuchElementException</code>.</p>
</li>
<li>
<p>Tiếp theo, lấy phần tử head thực sự thông qua <code class="notranslate" translate="no">internalGet(head)</code> (để return cho caller).
Sau đó, gán <code class="notranslate" translate="no">null</code> vào vị trí head trong <code class="notranslate" translate="no">elementData</code> để clear reference (để GC có thể thu hồi nếu cần).</p>
</li>
<li>
<p>Tiếp đến, quan trọng nhất, đó chính là việc đẩy <code class="notranslate" translate="no">head</code> về sau một vị trí, hiểu theo nghĩa logical index là <code class="notranslate" translate="no">head = head + 1</code>.
Tương tự như <code class="notranslate" translate="no">addFirst</code> có <code class="notranslate" translate="no">decremented</code>, thì <code class="notranslate" translate="no">removeFirst</code> có <code class="notranslate" translate="no">incremented</code> để <code class="notranslate" translate="no">+1</code> nhưng wrap-around.
<code class="notranslate" translate="no">incremented</code> chính là dual của <code class="notranslate" translate="no">decremented</code>, cũng có cơ chế wrap-around qua 0 nếu <code class="notranslate" translate="no">head</code> đang ở cuối array.</p>
</li>
<li>
<p>Tiếp theo, giảm <code class="notranslate" translate="no">size</code> đi 1 để reflect việc đã remove 1 phần tử.</p>
</li>
<li>
<p>Sau cùng, đơn giản chỉ return phần tử đã remove.</p>
</li>
</ul>
<p>Ta lấy ví dụ <code class="notranslate" translate="no">head</code> ở cuối array và ta thực hiện <code class="notranslate" translate="no">removeFirst</code> thì <code class="notranslate" translate="no">head</code> sẽ wrap về đầu array:</p>
<pre class="notranslate" translate="no"><code class="notranslate" translate="no">[e1, e2, ##, ##, e0]
         ↑       ↑
        tail    head
# size = 3, head = 4, tail = 2, capacity = 5

-------- removeFirst() --------

[e1, e2, ##, ##, ##]
 ↑       ↑
head    tail
# size = 2, head = 0, tail = 2, capacity = 5
</code></pre>
<h3>II.4. removeLast</h3>
<pre class="notranslate language-kotlin" translate="no"><code class="notranslate language-kotlin" translate="no"><span class="token keyword">class</span> ArrayDeque<span class="token operator">&lt;</span>E<span class="token operator">&gt;</span> <span class="token operator">:</span> AbstractMutableList<span class="token operator">&lt;</span>E<span class="token operator">&gt;</span> <span class="token punctuation">{</span>
  <span class="token comment">/**
   * Removes the last element from this deque and returns that removed element, or throws [NoSuchElementException] if this deque is empty.
   */</span>
  <span class="token annotation builtin">@IgnorableReturnValue</span>
  <span class="token keyword">public</span> <span class="token keyword">fun</span> <span class="token function">removeLast</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> E <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">isEmpty</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">throw</span> <span class="token function">NoSuchElementException</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"ArrayDeque is empty."</span></span><span class="token punctuation">)</span>
    <span class="token function">registerModification</span><span class="token punctuation">(</span><span class="token punctuation">)</span>

    <span class="token keyword">val</span> internalLastIndex <span class="token operator">=</span> <span class="token function">internalIndex</span><span class="token punctuation">(</span>lastIndex<span class="token punctuation">)</span>
    <span class="token keyword">val</span> element <span class="token operator">=</span> <span class="token function">internalGet</span><span class="token punctuation">(</span>internalLastIndex<span class="token punctuation">)</span>
    elementData<span class="token punctuation">[</span>internalLastIndex<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token keyword">null</span>
    size <span class="token operator">-=</span> <span class="token number">1</span>
    <span class="token keyword">return</span> element
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<ul>
<li>
<p>Tương tự như <code class="notranslate" translate="no">removeFirst</code>, nó sẽ check nếu <code class="notranslate" translate="no">ArrayDeque</code> đang rỗng thì sẽ throw <code class="notranslate" translate="no">NoSuchElementException</code>.</p>
</li>
<li>
<p>Lấy <em>physical index</em> của phần tử cuối <code class="notranslate" translate="no">lastIndex</code> - logical index, thông qua <code class="notranslate" translate="no">internalLastIndex = internalIndex(lastIndex)</code>. Sau đó, lấy phần tử cuối thực sự thông qua <code class="notranslate" translate="no">element = internalGet(internalLastIndex)</code> (để return cho caller).</p>
</li>
<li>
<p>Gán <code class="notranslate" translate="no">null</code> vào <code class="notranslate" translate="no">internalLastIndex</code> trong <code class="notranslate" translate="no">elementData</code> để clear reference (để GC có thể thu hồi nếu cần).</p>
</li>
<li>
<p>Giảm <code class="notranslate" translate="no">size</code> đi 1 để reflect việc đã remove 1 phần tử.</p>
</li>
<li>
<p>Sau cùng, đơn giản chỉ return phần tử đã remove.</p>
</li>
</ul>
<p>Ta thử lấy ví dụ phần tử cuối ở đầu array và ta thực hiện <code class="notranslate" translate="no">removeLast</code>:</p>
<pre class="notranslate" translate="no"><code class="notranslate" translate="no">[e1, ##, ##, ##, e0]
     ↑           ↑
    tail        head
# size = 2, head = 4, tail = 1, capacity = 5

-------- removeLast() --------

[##, ##, ##, ##, e0]
 ↑               ↑
tail            head
# size = 1, head = 4, tail = 0, capacity = 5
</code></pre>
<hr>
<h2>III. Capacity expansion</h2>
<h3>III.1. ensureCapacity và copyElements</h3>
<p>Method <code class="notranslate" translate="no">ensureCapacity</code> sẽ đảm nhận nhiệm vụ mở rộng capacity của <code class="notranslate" translate="no">elementData</code> khi cần thiết, cụ thể là khi <code class="notranslate" translate="no">addFirst</code> hoặc <code class="notranslate" translate="no">addLast</code> được gọi mà <code class="notranslate" translate="no">size + 1 &gt; elementData.size</code> (tức là capacity hiện tại không đủ để chứa phần tử mới).</p>
<pre class="notranslate language-kotlin" translate="no"><code class="notranslate language-kotlin" translate="no"><span class="token keyword">class</span> ArrayDeque<span class="token operator">&lt;</span>E<span class="token operator">&gt;</span> <span class="token operator">:</span> AbstractMutableList<span class="token operator">&lt;</span>E<span class="token operator">&gt;</span> <span class="token punctuation">{</span>
  <span class="token comment">/**
   * Constructs an empty deque with specified [initialCapacity], or throws [IllegalArgumentException] if [initialCapacity] is negative.
   */</span>
  <span class="token keyword">public</span> <span class="token keyword">constructor</span><span class="token punctuation">(</span>initialCapacity<span class="token operator">:</span> Int<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    elementData <span class="token operator">=</span> <span class="token keyword">when</span> <span class="token punctuation">{</span>
      initialCapacity <span class="token operator">==</span> <span class="token number">0</span> <span class="token operator">-&gt;</span> emptyElementData
      initialCapacity <span class="token operator">&gt;</span> <span class="token number">0</span> <span class="token operator">-&gt;</span> <span class="token function">arrayOfNulls</span><span class="token punctuation">(</span>initialCapacity<span class="token punctuation">)</span>
      <span class="token keyword">else</span> <span class="token operator">-&gt;</span> <span class="token keyword">throw</span> <span class="token function">IllegalArgumentException</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"Illegal Capacity: </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$</span><span class="token expression">initialCapacity</span></span><span class="token string">"</span></span><span class="token punctuation">)</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>

  <span class="token comment">/**
   * Constructs an empty deque.
   */</span>
  <span class="token keyword">public</span> <span class="token keyword">constructor</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    elementData <span class="token operator">=</span> emptyElementData
  <span class="token punctuation">}</span>

  <span class="token comment">/**
   * Ensures that the capacity of this deque is at least equal to the specified [minCapacity].
   *
   * If the current capacity is less than the [minCapacity], a new backing storage is allocated with greater capacity.
   * Otherwise, this method takes no action and simply returns.
   */</span>
  <span class="token keyword">private</span> <span class="token keyword">fun</span> <span class="token function">ensureCapacity</span><span class="token punctuation">(</span>minCapacity<span class="token operator">:</span> Int<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>minCapacity <span class="token operator">&lt;</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token keyword">throw</span> <span class="token function">IllegalStateException</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"Deque is too big."</span></span><span class="token punctuation">)</span>    <span class="token comment">// overflow</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>minCapacity <span class="token operator">&lt;=</span> elementData<span class="token punctuation">.</span>size<span class="token punctuation">)</span> <span class="token keyword">return</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>elementData <span class="token operator">===</span> emptyElementData<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      elementData <span class="token operator">=</span> <span class="token function">arrayOfNulls</span><span class="token punctuation">(</span>minCapacity<span class="token punctuation">.</span><span class="token function">coerceAtLeast</span><span class="token punctuation">(</span>defaultMinCapacity<span class="token punctuation">)</span><span class="token punctuation">)</span>
      <span class="token keyword">return</span>
    <span class="token punctuation">}</span>

    <span class="token comment">// fun newCapacity(oldCapacity: Int, minCapacity: Int): Int</span>
    <span class="token keyword">val</span> newCapacity <span class="token operator">=</span> AbstractList<span class="token punctuation">.</span><span class="token function">newCapacity</span><span class="token punctuation">(</span>elementData<span class="token punctuation">.</span>size<span class="token punctuation">,</span> minCapacity<span class="token punctuation">)</span>
    <span class="token function">copyElements</span><span class="token punctuation">(</span>newCapacity<span class="token punctuation">)</span>
  <span class="token punctuation">}</span>

  <span class="token comment">/**
   * Creates a new array with the specified [newCapacity] size and copies elements in the [elementData] array to it.
   */</span>
  <span class="token keyword">private</span> <span class="token keyword">fun</span> <span class="token function">copyElements</span><span class="token punctuation">(</span>newCapacity<span class="token operator">:</span> Int<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">val</span> newElements <span class="token operator">=</span> arrayOfNulls<span class="token operator">&lt;</span>Any<span class="token operator">?</span><span class="token operator">&gt;</span><span class="token punctuation">(</span>newCapacity<span class="token punctuation">)</span>
    <span class="token comment">// fun Array&lt;out T&gt;.copyInto(destination: Array&lt;T&gt;, destinationOffset: Int = 0, startIndex: Int = 0, endIndex: Int = size): Array&lt;T&gt;</span>
    elementData<span class="token punctuation">.</span><span class="token function">copyInto</span><span class="token punctuation">(</span>newElements<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> head<span class="token punctuation">,</span> elementData<span class="token punctuation">.</span>size<span class="token punctuation">)</span>
    elementData<span class="token punctuation">.</span><span class="token function">copyInto</span><span class="token punctuation">(</span>newElements<span class="token punctuation">,</span> elementData<span class="token punctuation">.</span>size <span class="token operator">-</span> head<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> head<span class="token punctuation">)</span>
    head <span class="token operator">=</span> <span class="token number">0</span>
    elementData <span class="token operator">=</span> newElements
  <span class="token punctuation">}</span>

  <span class="token keyword">internal</span> <span class="token keyword">companion</span> <span class="token keyword">object</span> <span class="token punctuation">{</span>
    <span class="token keyword">private</span> <span class="token keyword">val</span> emptyElementData <span class="token operator">=</span> emptyArray<span class="token operator">&lt;</span>Any<span class="token operator">?</span><span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token keyword">private</span> <span class="token keyword">const</span> <span class="token keyword">val</span> defaultMinCapacity <span class="token operator">=</span> <span class="token number">10</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<ul>
<li>
<p>Nếu <code class="notranslate" translate="no">minCapacity &lt; 0</code> tức là minCapacity đã vượt quá biên trên <code class="notranslate" translate="no">Int.MAX_VALUE</code> nên value nó bị thành âm (overflow), lúc này sẽ throw <code class="notranslate" translate="no">IllegalStateException</code> với message "Deque is too big.".</p>
</li>
<li>
<p>Nếu <code class="notranslate" translate="no">minCapacity &lt;= elementData.size</code> tức là capacity hiện tại đã đủ lớn để chứa phần tử mới, thì method sẽ không làm gì cả và return luôn.</p>
</li>
<li>
<p>Nếu <code class="notranslate" translate="no">elementData === emptyElementData</code> — ở đây <code class="notranslate" translate="no">emptyElementData</code> là một <strong>sentinel value</strong> dùng để biểu diễn trạng thái backing storage chưa được allocate thực sự,
thường xảy ra khi <code class="notranslate" translate="no">ArrayDeque</code> được tạo bằng <code class="notranslate" translate="no">constructor()</code> rỗng — thì sẽ tạo một array mới với capacity là <code class="notranslate" translate="no">max(minCapacity, defaultMinCapacity = 10)</code>,
tức là ít nhất 10 phần tử trong lần cấp phát đầu tiên.</p>
</li>
<li>
<p>Trường hợp cuối cùng, ta phải alloc một array mới với <code class="notranslate" translate="no">newCapacity = AbstractList.newCapacity</code> thông qua <code class="notranslate" translate="no">copyElements</code>.
Method <code class="notranslate" translate="no">copyElements</code> có nhiệm vụ alloc một array với <code class="notranslate" translate="no">length = newCapacity</code>, sau đó copy phần tử từ <code class="notranslate" translate="no">elementData</code> sang <code class="notranslate" translate="no">newElements</code> theo nguyên tắc sau:</p>
<ul>
<li>đầu tiên là các items từ <code class="notranslate" translate="no">head</code> đến <code class="notranslate" translate="no">elementData.size - 1</code></li>
<li>rồi tiếp theo là các items từ <code class="notranslate" translate="no">0</code> đến <code class="notranslate" translate="no">head - 1</code>.
Sau khi copy xong thì <code class="notranslate" translate="no">head</code> sẽ được reset về 0, và <code class="notranslate" translate="no">elementData</code> sẽ trỏ sang array mới <code class="notranslate" translate="no">newElements</code>.</li>
</ul>
</li>
</ul>
<p>Điểm nổi bật là sau khi ta “rải” các items từ array cũ sang <code class="notranslate" translate="no">newElements</code> thì layout của <code class="notranslate" translate="no">ArrayDeque</code> sẽ được reset về contiguous layout,
nghĩa là <code class="notranslate" translate="no">head</code> sẽ ở vị trí 0 và <code class="notranslate" translate="no">tail</code> sẽ ở vị trí <code class="notranslate" translate="no">size</code>, điều này giúp cho trạng thái buffer sạch hơn.</p>
<blockquote>
<p>Resize không chỉ tăng capacity, mà còn “duỗi thẳng” circular layout về lại linear layout.</p>
</blockquote>
<h3>III.2. Tại sao lại tăng capacity lên 1.5x?</h3>
<p>Ta thử tìm hiểu tiếp xem new capacity được tính như nào thông qua <code class="notranslate" translate="no">AbstractList.newCapacity</code>:</p>
<pre class="notranslate language-kotlin" translate="no"><code class="notranslate language-kotlin" translate="no"><span class="token keyword">public</span> <span class="token keyword">abstract</span> <span class="token keyword">class</span> AbstractList<span class="token operator">&lt;</span><span class="token keyword">out</span> E<span class="token operator">&gt;</span> <span class="token keyword">protected</span> <span class="token keyword">constructor</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">:</span> AbstractCollection<span class="token operator">&lt;</span>E<span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> List<span class="token operator">&lt;</span>E<span class="token operator">&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">internal</span> <span class="token keyword">companion</span> <span class="token keyword">object</span> <span class="token punctuation">{</span>
    <span class="token comment">/** [oldCapacity] and [minCapacity] must be non-negative. */</span>
    <span class="token keyword">internal</span> <span class="token keyword">fun</span> <span class="token function">newCapacity</span><span class="token punctuation">(</span>oldCapacity<span class="token operator">:</span> Int<span class="token punctuation">,</span> minCapacity<span class="token operator">:</span> Int<span class="token punctuation">)</span><span class="token operator">:</span> Int <span class="token punctuation">{</span>
      <span class="token comment">// overflow-conscious</span>
      <span class="token keyword">var</span> newCapacity <span class="token operator">=</span> oldCapacity <span class="token operator">+</span> <span class="token punctuation">(</span>oldCapacity <span class="token operator">shr</span> <span class="token number">1</span><span class="token punctuation">)</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span>newCapacity <span class="token operator">-</span> minCapacity <span class="token operator">&lt;</span> <span class="token number">0</span><span class="token punctuation">)</span>
        newCapacity <span class="token operator">=</span> minCapacity
      <span class="token keyword">if</span> <span class="token punctuation">(</span>newCapacity <span class="token operator">-</span> maxArraySize <span class="token operator">&gt;</span> <span class="token number">0</span><span class="token punctuation">)</span>
        newCapacity <span class="token operator">=</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>minCapacity <span class="token operator">&gt;</span> maxArraySize<span class="token punctuation">)</span> Int<span class="token punctuation">.</span>MAX_VALUE <span class="token keyword">else</span> maxArraySize
      <span class="token keyword">return</span> newCapacity
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<p>Ta focus vào dòng <code class="notranslate" translate="no">var newCapacity = oldCapacity + (oldCapacity shr 1)</code>,
ở đây có thể hiểu là <code class="notranslate" translate="no">newCapacity = oldCapacity + oldCapacity/2</code> (sử dụng “shift right” để chia cho 2),
nên <code class="notranslate" translate="no">newCapacity = oldCapacity * 1.5</code>.
Đây là một chiến lược phổ biến để tăng capacity của các data structure dạng array,
vì nó giúp cân bằng giữa việc <strong>giảm số lần mở rộng</strong> (vì mỗi lần mở rộng sẽ tăng capacity lên 50%)
và <strong>tránh lãng phí quá nhiều memory</strong> (so với việc tăng gấp đôi capacity).</p>
<p>Câu hỏi đặt ra là:
“Tại sao không sử dụng 2x mà lại là 1.5x”?
2x, thường thấy trong <code class="notranslate" translate="no">std::vector</code> của trình biên dịch GCC - C++, sẽ đỡ phải copy nhiều hơn, nhưng nó cũng sẽ lãng phí nhiều memory hơn,
đặc biệt là khi <code class="notranslate" translate="no">oldCapacity</code> đã lớn,
có thể lãng phí đến gần 50% bộ nhớ ở lần cấp phát cuối cùng.</p>
<p>Còn 1.5x (dùng trong Java <code class="notranslate" translate="no">ArrayList</code>, Kotlin <code class="notranslate" translate="no">ArrayDeque</code>, hoặc <code class="notranslate" translate="no">std::vector</code> của MSVC) là “điểm ngọt” (sweet spot),
không lãng phí quá nhiều memory,
giữ cho lượng RAM lãng phí ở mức tối đa chỉ khoảng 33%,
nhưng ta chấp nhận phải copy nhiều hơn một chút.
Đây chính là một trade-off giữa <strong>performance</strong> và <strong>memory efficiency</strong>.</p>
<p>Hơn nữa, về mặt lý thuyết, việc chọn hệ số 1.5x thay vì 2x cũng có thể giúp tăng khả năng tái sử dụng bộ nhớ trong một số mô hình cấp phát.
Vấn đề này có thể được chứng minh qua toán học khi xét ở lần cấp phát thứ <code class="notranslate" translate="no">n</code>:</p>
<ul>
<li>
<p>Trường hợp dùng 2x: Size của mảng mới cần cấp phát là <code class="notranslate" translate="no">2^n</code>.
Tuy nhiên, con số này luôn <strong>lớn hơn</strong> 1 đơn vị so với tổng size của tất cả các mảng cũ đã bị vứt bỏ trước đó (<code class="notranslate" translate="no">sum(2^i, i=0..n-1) = 2^n - 1</code>).
Điều này làm giảm khả năng tái sử dụng trực tiếp các vùng nhớ cũ theo mô hình lý thuyết.</p>
</li>
<li>
<p>Trường hợp dùng 1.5x: Size của mảng mới là <code class="notranslate" translate="no">1.5^n</code>.
Khác với hệ số 2, từ lần cấp phát thứ <strong>2</strong> trở đi, kích thước này sẽ <strong>nhỏ hơn</strong> tổng size của bộ nhớ cũ đã giải phóng (<code class="notranslate" translate="no">sum(1.5^i, i=0..n-1)</code>).
Nhờ vậy, về mặt lý thuyết Memory Allocator có nhiều cơ hội hơn để tái sử dụng các vùng nhớ đã được giải phóng.</p>
</li>
</ul>
<p>Tất nhiên, trong thực tế chuyện allocator có tái sử dụng được vùng nhớ cũ hay không còn phụ thuộc vào runtime,
GC, allocator và platform cụ thể. Vì vậy, nên xem đây là một intuition về trade-off, không phải một guarantee tuyệt đối.</p>
<hr>
<h2>IV. Complexity Analysis</h2>
<h3>IV.1. Get operations</h3>
<p>Vì <code class="notranslate" translate="no">ArrayDeque</code> vẫn map <em>logical index</em> sang <em>physical index</em> bằng <code class="notranslate" translate="no">internalIndex(index)</code>,
nên <code class="notranslate" translate="no">get(index)</code> vẫn là O(1).</p>
<p>Tuy nhiên, các thao tác insert/remove ở giữa deque không còn O(1),
vì lúc đó phải dịch chuyển elements ở một phía để giữ layout hợp lệ.</p>
<h3>IV.2. Remove first/last operations</h3>
<p>Các method remove phần tử như <code class="notranslate" translate="no">removeFirst</code> và <code class="notranslate" translate="no">removeLast</code> đều có complexity là O(1),
vì chúng chỉ clear slot tương ứng, cập nhật <code class="notranslate" translate="no">head</code>/<code class="notranslate" translate="no">size</code>, và không cần loop hay copy elements.</p>
<h3>IV.3. Add first/last operations</h3>
<p>Các method add phần tử như <code class="notranslate" translate="no">addFirst</code> và <code class="notranslate" translate="no">addLast</code> có complexity amortized O(1).</p>
<p>Trong phần lớn trường hợp, chúng chỉ cần ghi phần tử vào vị trí tương ứng trong backing array rồi cập nhật <code class="notranslate" translate="no">head</code>/<code class="notranslate" translate="no">size</code>.
Rõ ràng, đây chính là O(1) cho mỗi lần insert.</p>
<p>Tuy nhiên, khi capacity không đủ, <code class="notranslate" translate="no">ArrayDeque</code> phải allocate array mới và copy toàn bộ elements sang array mới, lúc này chi phí là O(N).
Dù vậy, vì mỗi lần mở rộng capacity tăng theo hệ số cố định khoảng 1.5x,
resize <strong>không xảy ra ở mọi lần insert</strong> mà chỉ diễn ra khi capacity hiện tại đã đầy.</p>
<p>Tổng chi phí copy qua N lần insert tạo thành một chuỗi hình học và vẫn bị chặn bởi O(N).
Cụ thể, giả sử capacity bắt đầu là <code class="notranslate" translate="no">c</code> và mỗi lần resize tăng theo hệ số <code class="notranslate" translate="no">r = 1.5</code>, thì các lần resize sẽ phải copy số phần tử xấp xỉ:</p>
<pre class="notranslate language-text" translate="no"><code class="notranslate language-text" translate="no">c + cr + cr² + cr³ + ... + crᵏ
</code></pre>
<p>Đây là một chuỗi hình học - là dãy số mà mỗi số sau bằng số trước nhân với một hằng số.</p>
<p>Sau lần resize cuối, capacity <code class="notranslate" translate="no">crᵏ</code> vừa đủ để chứa N phần tử nên chắc chắn <code class="notranslate" translate="no">N &lt;= crᵏ</code>.
Nhưng nó cũng không thể quá lớn hơn N nhiều, vì trước lần resize đó, capacity cũ là <code class="notranslate" translate="no">crᵏ⁻¹</code> và capacity cũ chưa đủ chứa N, nên <code class="notranslate" translate="no">crᵏ⁻¹ &lt; N</code> tương đương <code class="notranslate" translate="no">crᵏ &lt; rN</code>. Do đó, ta suy ra số hạng cuối <code class="notranslate" translate="no">crᵏ</code> cũng cùng bậc với N:</p>
<pre class="notranslate language-text" translate="no"><code class="notranslate language-text" translate="no">N &lt;= crᵏ &lt; rN
</code></pre>
<p>Dựa theo công thức tính tổng cấp số nhân, vì <code class="notranslate" translate="no">r &gt; 1</code> là hằng số, nên tổng của chuỗi hình học luôn cùng bậc với số hạng lớn nhất:</p>
<pre class="notranslate language-text" translate="no"><code class="notranslate language-text" translate="no">c + cr + cr² + ... + crᵏ
 = c(rᵏ⁺¹ - 1) / (r - 1)
 = O(crᵏ)
 = O(N)
</code></pre>
<p>Nói cách khác, dù một vài lần insert phải trả giá đắt vì resize/copy, tổng chi phí copy sau N lần insert vẫn chỉ tăng tuyến tính theo N.
Vì vậy, khi chia đều chi phí đó cho N lần insert, mỗi lần insert có chi phí trung bình là amortized O(1).</p>
<hr>
<h2>V. Takeaways</h2>
<ul>
<li>
<p>ArrayDeque được implement bằng <em>resizable circular buffer</em>.</p>
<ul>
<li>Chỉ cần 3 state chính: <code class="notranslate" translate="no">elementData</code>, <code class="notranslate" translate="no">head</code> và <code class="notranslate" translate="no">size</code>.</li>
<li><code class="notranslate" translate="no">tail</code> không được lưu trữ mà được derived từ <code class="notranslate" translate="no">head</code> và <code class="notranslate" translate="no">size</code>.</li>
<li>Capacity tăng khoảng 1.5x để cân bằng giữa performance và memory efficiency.</li>
<li>Khi resize, layout sẽ được normalize về contiguous form với <code class="notranslate" translate="no">head</code> = 0.</li>
</ul>
</li>
<li>
<p><code class="notranslate" translate="no">removeFirst</code> và <code class="notranslate" translate="no">removeLast</code> là O(1).</p>
</li>
<li>
<p><code class="notranslate" translate="no">addFirst</code> và <code class="notranslate" translate="no">addLast</code> là amortized O(1).</p>
</li>
</ul>
<blockquote>
<p>Nói ngắn gọn: ArrayDeque nhanh không phải vì nó “ma thuật”, mà vì nó né được việc shift elements bằng cách xoay index thay vì xoay data.</p>
</blockquote>]]></content:encoded>
            <author>hoc081098@gmail.com (Petrus Nguyễn Thái Học (hoc081098))</author>
        </item>
        <item>
            <title><![CDATA[Rate limiting strategies for Nginx, API Gateway, and service layers]]></title>
            <link>https://portfolio.hoc081098.dev/articles/rate-limiting-strategies</link>
            <guid>https://portfolio.hoc081098.dev/articles/rate-limiting-strategies</guid>
            <pubDate>Sat, 14 Feb 2026 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Rate limiting không nên được đặt ở một tầng duy nhất. Trong một hệ thống backend có phân tầng rõ ràng, rate limiting
thường xuất hiện ở ba vị trí: Nginx (edge), API Gateway và từng service cụ thể. Mỗi tầng có mục tiêu khác nhau, và nếu
phân định đúng, chúng bổ trợ cho nhau thay vì chồng chéo.</p>
<pre class="notranslate" translate="no"><code class="notranslate" translate="no">Internet
   ↓
Nginx (IP flood protection)
   ↓
YARP (user / tenant policy limit)
   ↓
Service (domain limit / concurrency limit)
</code></pre>
<h2>1. Nginx - tầng hạ tầng (edge / infra level)</h2>
<ul>
<li>Config các basic rate limiting nhằm mục đích chống spam/DDoS, chống flood thô.
Đây là lớp phòng thủ đầu tiên trước khi request chạm tới business logic.
Nó không hiểu JWT là gì, không biết UserId hay TenantId nào đang gọi. Nó chỉ nhìn thấy network-level metadata.</li>
<li>Ví dụ: limit theo IP address, Geo block (theo quốc gia/vùng), hoặc giới hạn request rate và connection ở mức IP</li>
<li>Mục tiêu: network-level protection - chặn rác ở tầng infra/network, trước khi đi sâu vào business logic</li>
</ul>
<pre class="notranslate language-nginx" translate="no"><code class="notranslate language-nginx" translate="no"><span class="token directive"><span class="token keyword">http</span></span> <span class="token punctuation">{</span>
    <span class="token comment"># ... other http settings ...</span>
    <span class="token directive"><span class="token keyword">limit_req_zone</span> <span class="token variable">$binary_remote_addr</span> zone=mylimit:10m rate=5r/s</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token directive"><span class="token keyword">server</span></span> <span class="token punctuation">{</span>
    <span class="token comment"># ... other server settings ...</span>

    <span class="token directive"><span class="token keyword">limit_req_status</span> <span class="token number">429</span></span><span class="token punctuation">;</span> <span class="token comment"># Return "429 Too Many Requests" error</span>
    <span class="token directive"><span class="token keyword">location</span> /login/</span> <span class="token punctuation">{</span>
        <span class="token directive"><span class="token keyword">limit_req</span> zone=mylimit burst=10 nodelay</span><span class="token punctuation">;</span>
        <span class="token comment"># ... other location settings, e.g., proxy_pass ...</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<ul>
<li>
<p>Sử dụng <code class="notranslate" translate="no">limit_req_zone</code> directive trong <code class="notranslate" translate="no">http</code> block với</p>
<ul>
<li><code class="notranslate" translate="no">$binary_remote_addr</code>: sử dụng IP address của client trong dạng nhị phân để làm KEY.</li>
<li><code class="notranslate" translate="no">zone=mylimit:10m</code>: tạo 1 zone tên là "mylimit" có size <code class="notranslate" translate="no">10MB</code> memory (có thể lưu trữ trạng thái của khoảng 160,000 địa chỉ IP)</li>
<li><code class="notranslate" translate="no">rate=5r/s</code>: giới hạn tốc độ là 5 requests/giây cho mỗi IP address.</li>
</ul>
</li>
<li>
<p>Sử dụng <code class="notranslate" translate="no">limit_req</code> directive trong <code class="notranslate" translate="no">location</code> block để áp dụng limit đã định nghĩa ở trên.</p>
<ul>
<li><code class="notranslate" translate="no">zone=mylimit</code>: chỉ định zone đã tạo ở trên</li>
<li><code class="notranslate" translate="no">burst=10</code>: Thiết lập một "hàng đợi" dự phòng cho phép tối đa 10 request vượt định mức.
Thay vì bị từ chối ngay lập tức khi vượt quá rate, các request này sẽ được tạm giữ lại trong bộ nhớ của Nginx.</li>
<li>Mặc định (không có <code class="notranslate" translate="no">nodelay</code>): Nginx sẽ "điều tiết" lưu lượng bằng cách áp đặt độ trễ (delay).
Các request trong hàng đợi <code class="notranslate" translate="no">burst</code> sẽ bị ép phải xếp hàng và đi qua cửa kiểm soát từng cái một theo đúng nhịp độ của rate.
Kết quả là người dùng sẽ thấy ứng dụng bị chậm lại (latency cao) nhưng không bị lỗi.
<code class="notranslate" translate="no">nodelay</code>: Khi kích hoạt, Nginx sẽ ưu tiên trải nghiệm người dùng bằng cách cho phép các request trong hàng đợi
được xử lý ngay lập tức mà không cần chờ đợi.
Tuy nhiên, mỗi request "đi tắt" này vẫn sẽ chiếm dụng một slot trong hàng đợi burst và chỉ được giải phóng (hồi slot) theo đúng tốc độ của rate.</li>
<li>Lưu ý: Chỉ khi hàng đợi dự phòng này bị lấp đầy hoàn toàn (vượt quá cả <code class="notranslate" translate="no">rate</code> lẫn <code class="notranslate" translate="no">burst</code>),
Nginx mới bắt đầu từ chối bằng status code đã cấu hình (ví dụ: 429).</li>
</ul>
</li>
</ul>
<h2>2. API Gateway (ví dụ YARP)</h2>
<ul>
<li>Config rate limiting chung cho các services phía sau. Tầng này có thể parse và hiểu JWT, UserId, TenantId, API key,
API version. Khác với Nginx, gateway hiểu "ai đang gọi", không chỉ "IP nào đang gọi".
Vì vậy, nó phù hợp để enforce các chính sách truy cập và quota mang tính hệ thống.</li>
<li>Ví dụ: limit theo UserId, TenantId, apply quota theo API key, API version, Quota theo app.</li>
<li>Mục tiêu: policy-level control - bảo vệ tài nguyên theo các rules/policies chung cho nhiều services.</li>
</ul>
<h2>3. Service: tầng business logic cụ thể</h2>
<ul>
<li>Khi request đi tới service, rate limiting không còn đơn thuần là bảo vệ hạ tầng hay thực thi policy chung nữa, mà gắn trực tiếp với domain.
Ở đây, ta config rate limiting riêng cho service đó, dựa trên business logic đặc thù của service.</li>
<li>Ví dụ: một user chỉ được tạo 5 order/phút, một tenant chỉ được gửi 1,000 webhook/phút, chỉ cho phép 10 concurrent job
xử lý</li>
<li>Mục tiêu: business-level control - bảo vệ tài nguyên dựa trên logic cụ thể.</li>
</ul>
<h2>4. Rate Limiting Algorithms trong ASP.NET Core (có thể dùng cho YARP và Services)</h2>
<ul>
<li>Fixed Window: Đơn giản, giới hạn số requests trong 1 window cố định. Nhưng dễ bị spike ở window boundary.</li>
<li>Sliding Window: Cải tiến hơn Fixed Window. Giới hạn N request/X giây gần nhất bằng cách chia Window thành nhiều segment cố định, mỗi khi check sẽ đếm số request của segment hiện tại với các segment trước đó.
Giảm spike tại window boundary. Nhưng tốn kém hơn về mặt memory vì phải lưu nhiều segment.
Đọc thêm tại <a href="https://www.facebook.com/hoc081098/posts/pfbid02XBGAAW7iu8SymptgHhxERbfwri4RQGNGJaJ4kR72vv4iUu6bGTtUJ6q3HPbQP2Wgl">bài viết trên Facebook</a></li>
<li>Token Bucket: Mỗi request sẽ "rút" token từ bucket. Bucket được refill theo thời gian.
Cho phép burst trong 1 thời gian ngắn &amp; giữ tốc độ trung bình trong thời gian dài.
Phù hợp với các hệ thống cần cho phép burst nhưng vẫn kiểm soát lưu lượng trung bình.
Đọc thêm tại <a href="https://www.facebook.com/hoc081098/posts/pfbid02s3RrBtD93FminminepcLM4GBhkADZ95bmjwz9GXWAsYMWDgXUc1uRnVYuwRwojRwl">bài viết trên Facebook</a></li>
<li>Concurrency: Giới hạn số requests đang được xử lý đồng thời tại 1 thời điểm.
Không giới hạn số request theo thời gian, mà giới hạn số request đang xử lý đồng thời.
Phù hợp với các hệ thống có tài nguyên hạn chế hoặc cần đảm bảo hiệu suất ổn định.</li>
</ul>
<p>Một đoạn code ví dụ về cách cấu hình Rate Limiting trong YARP:</p>
<p>File <code class="notranslate" translate="no">Program.cs</code></p>
<pre class="notranslate language-csharp" translate="no"><code class="notranslate language-csharp" translate="no"><span class="token comment">// 1. Add Rate Limiter.</span>
services<span class="token punctuation">.</span><span class="token function">AddRateLimiter</span><span class="token punctuation">(</span>options <span class="token operator">=&gt;</span>
<span class="token punctuation">{</span>
    options<span class="token punctuation">.</span><span class="token function">AddFixedWindowLimiter</span><span class="token punctuation">(</span><span class="token string">"customPolicy"</span><span class="token punctuation">,</span> opt <span class="token operator">=&gt;</span>
    <span class="token punctuation">{</span>
        opt<span class="token punctuation">.</span>PermitLimit <span class="token operator">=</span> <span class="token number">4</span><span class="token punctuation">;</span>
        opt<span class="token punctuation">.</span>Window <span class="token operator">=</span> TimeSpan<span class="token punctuation">.</span><span class="token function">FromSeconds</span><span class="token punctuation">(</span><span class="token number">12</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        opt<span class="token punctuation">.</span>QueueProcessingOrder <span class="token operator">=</span> QueueProcessingOrder<span class="token punctuation">.</span>OldestFirst<span class="token punctuation">;</span>
        opt<span class="token punctuation">.</span>QueueLimit <span class="token operator">=</span> <span class="token number">2</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 2. add the RateLimiter middleware.</span>
app<span class="token punctuation">.</span><span class="token function">UseRateLimiter</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

app<span class="token punctuation">.</span><span class="token function">MapReverseProxy</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<p>File <code class="notranslate" translate="no">appsettings.json</code> - Apply <code class="notranslate" translate="no">customPolicy</code> rate limit policy cho route1:</p>
<pre class="notranslate language-json" translate="no"><code class="notranslate language-json" translate="no"><span class="token punctuation">{</span>
  <span class="token property">"ReverseProxy"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token property">"Routes"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
      <span class="token property">"route1"</span> <span class="token operator">:</span> <span class="token punctuation">{</span>
        <span class="token property">"ClusterId"</span><span class="token operator">:</span> <span class="token string">"cluster1"</span><span class="token punctuation">,</span>
        <span class="token property">"RateLimiterPolicy"</span><span class="token operator">:</span> <span class="token string">"customPolicy"</span><span class="token punctuation">,</span>
        <span class="token property">"Match"</span><span class="token operator">:</span> <span class="token punctuation">{</span><span class="token property">"Hosts"</span><span class="token operator">:</span> <span class="token punctuation">[</span> <span class="token string">"localhost"</span> <span class="token punctuation">]</span><span class="token punctuation">}</span>
      <span class="token punctuation">}</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token property">"Clusters"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
      <span class="token property">"cluster1"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
        <span class="token property">"Destinations"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
          <span class="token property">"cluster1/destination1"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
            <span class="token property">"Address"</span><span class="token operator">:</span> <span class="token string">"https://localhost:10001/"</span>
          <span class="token punctuation">}</span>
        <span class="token punctuation">}</span>
      <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<p>Đọc thêm <a href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/servers/yarp/rate-limiting?view=aspnetcore-10.0">Microsoft docs: Rate Limiting in YARP</a></p>
<h2>Khi phân tầng đúng, chúng bổ trợ nhau</h2>
<ul>
<li>Edge (Nginx): stateless &amp; IP-based -&gt; chống rác ở network level</li>
<li>Gateway (YARP): identity-aware -&gt; enforce policy theo identity</li>
<li>Service: stateful &amp; domain-aware -&gt; enforce invariant theo domain</li>
</ul>
<p>Chúng chỉ overlap khi ranh giới không rõ ràng. Ví dụ, nếu Nginx limit quá chặt, request hợp lệ có thể bị chặn trước
khi tới gateway hoặc service, khiến các tầng phía dưới không còn ý nghĩa thực tế.
Hoặc nếu cùng một logic rate limiting bị định nghĩa trùng nhau ở cả Nginx và Gateway, hệ thống sẽ trở nên rối rắm và khó kiểm soát.
Rate limiting, nếu nhìn đúng bản chất, không phải một cấu hình duy nhất. Nó là một chiến lược phân tầng.
Và sự rõ ràng trong phân tầng mới là thứ quyết định hệ thống có gọn gàng hay trở thành spaghetti policy.</p>]]></content:encoded>
            <author>hoc081098@gmail.com (Petrus Nguyễn Thái Học (hoc081098))</author>
        </item>
        <item>
            <title><![CDATA[Bàn về Subject trong Rx (ví dụ với RxSwift 😇)]]></title>
            <link>https://portfolio.hoc081098.dev/articles/rx-subject-sync</link>
            <guid>https://portfolio.hoc081098.dev/articles/rx-subject-sync</guid>
            <pubDate>Sat, 17 Jun 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h2>I. Serially rule 😇</h2>
<p><code class="notranslate" translate="no">Observable</code> trong <code class="notranslate" translate="no">ReactiveX</code> phải thuân thủ quy tắc <code class="notranslate" translate="no">Serially</code>, tức là phải đảm bảo các sự kiện phát ra không được overlap
lên nhau. Quy tắc này không bắt buộc các event (signal) phải được delivered đến các subscriber ở cùng một thread.
Nó vẫn có thể được delivered đến các subscriber ở các thread khác nhau,
nhưng nó phải đảm bảo các event không được overlap lên nhau (tức phải đồng bộ thông qua các cơ chế như lock, atomic, ...).</p>
<p>Ví dụ: một Observable phát ra <code class="notranslate" translate="no">onNext(1)</code>, <code class="notranslate" translate="no">onNext(2)</code>, <code class="notranslate" translate="no">onNext(3)</code> thì 1 subscriber có thể nhận
<code class="notranslate" translate="no">onNext(1)</code> ở thread A, <code class="notranslate" translate="no">onNext(2)</code> ở thread B, <code class="notranslate" translate="no">onNext(3)</code> ở thread A,
nhưng nó không được phép nhận <code class="notranslate" translate="no">onNext(2)</code> trong khi <code class="notranslate" translate="no">onNext(1)</code> đang được delivered.</p>
<p>Hãy xem <a href="http://www.reactive-streams.org/">Reactive Streams Specification for the JVM</a> để thấy rõ hơn.</p>
<img alt="Serially" loading="lazy" width="2702" height="204" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Frxswift_sync_06.a018f918.png&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Frxswift_sync_06.a018f918.png&amp;w=3840&amp;q=75">
<img alt="Serially" loading="lazy" width="2630" height="426" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Frxswift_sync_07.681d50ee.png&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Frxswift_sync_07.681d50ee.png&amp;w=3840&amp;q=75">
<h2>II. Subjects are not thread-safe on the Observer side 🥺</h2>
<p><code class="notranslate" translate="no">Subject</code> không phải là thread-safe ở phía Observer.
Nếu chúng ta invoke <code class="notranslate" translate="no">onNext</code>, <code class="notranslate" translate="no">onError</code>, <code class="notranslate" translate="no">onCompleted</code> trên <code class="notranslate" translate="no">Subject</code> từ nhiều thread khác nhau thì có thể dẫn đến
các event bị overlap lên nhau, và điều này sẽ làm <code class="notranslate" translate="no">Subject</code> không đảm bảo quy tắc <code class="notranslate" translate="no">Serially</code> nữa.</p>
<p>Trong RxJava, tất cả Subject đều không thread-safe, ngoại trừ
<a href="https://github.com/ReactiveX/RxJava/blob/806ec1ca7d5ea50026f9019fc5b49ac70f7b1678/src/main/java/io/reactivex/rxjava3/subjects/SerializedSubject.java#L29"><code class="notranslate" translate="no">SerializedSubject</code></a>.
Chỉ cần gọi <code class="notranslate" translate="no">toSerialized()</code> trên 1 Subject bất kì là chúng ta đã có được một <code class="notranslate" translate="no">SerializedSubject</code>. <code class="notranslate" translate="no">SerializedSubject</code>
sẽ serialize các lời gọi tới method của Observer side, điều này được đảm bảo bằng 1 queue có type <code class="notranslate" translate="no">AppendOnlyLinkedArrayList</code>
được synchronized bởi chính <code class="notranslate" translate="no">SerializedSubject</code> đó, queue này sẽ giữ các event (signal/notification) bị missed,
để sau đó sẽ loop và deliver chúng đến các Observer một cách synchronized.</p>
<h3>1. ⚠️ Synchronization anomaly was detected</h3>
<p>Trong RxSwift, 4 loại Subject <code class="notranslate" translate="no">PublishSubject</code>, <code class="notranslate" translate="no">BehaviorSubject</code>, <code class="notranslate" translate="no">ReplaySubject</code>, <code class="notranslate" translate="no">AsyncSubject</code> đều không thread-safe.</p>
<p>✍️ Hãy lấy ví dụ với <code class="notranslate" translate="no">PublishSubject</code>, gọi <code class="notranslate" translate="no">onNext</code> trên <code class="notranslate" translate="no">PublishSubject</code> từ nhiều thread khác nhau cùng lúc.</p>
<p align="center"><img alt="Synchronization anomaly" loading="lazy" width="600" height="520" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Frxswift_sync_01.63f79e53.png&amp;w=640&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Frxswift_sync_01.63f79e53.png&amp;w=1200&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Frxswift_sync_01.63f79e53.png&amp;w=1200&amp;q=75"></p>
<blockquote>
<p>❌ Lỗi này xảy ra khi event thứ nhất <code class="notranslate" translate="no">onNext(1)</code> được send từ thread của <code class="notranslate" translate="no">queue-1</code>, <code class="notranslate" translate="no">onNext(2)</code> event sau lại được
send từ thread của <code class="notranslate" translate="no">queue-2</code> trong khi event 1 đang được delivered,tức vẫn chưa hoàn thành việc delivery event 1.</p>
</blockquote>
<p>🧐 RxSwift detect được chúng ta đang gọi từ nhiều thread khác nhau, sẽ log ra lỗi <code class="notranslate" translate="no">⚠️ Synchronization anomaly was detected</code>.
Nếu chúng ta enable flag <code class="notranslate" translate="no">FATAL_SYNCHRONIZATION</code>, thì RxSwift sẽ crash app thông qua <code class="notranslate" translate="no">fatalError</code>.</p>
<p>✅ Cách fix đơn giản nhất là tạo một <code class="notranslate" translate="no">Serial DispatchQueue</code>, và đưa các lời gọi tới <code class="notranslate" translate="no">PublishSubject</code> vào trong DispatchQueue đó.
Hoặc sử dụng một <code class="notranslate" translate="no">NsRecursiveLock</code> để đảm bảo các lời gọi tới Observer side của <code class="notranslate" translate="no">PublishSubject</code> được synchronized.</p>
<p align="center"><img alt="Serial DispatchQueue" loading="lazy" width="600" height="404" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Frxswift_sync_08.1d100501.png&amp;w=640&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Frxswift_sync_08.1d100501.png&amp;w=1200&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Frxswift_sync_08.1d100501.png&amp;w=1200&amp;q=75"></p>
<p><em>Serial DispatchQueue</em></p>
<br>
<br>
<p align="center"><img alt="NsRecursiveLock" loading="lazy" width="600" height="507" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Frxswift_sync_09.6274d9ff.png&amp;w=640&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Frxswift_sync_09.6274d9ff.png&amp;w=1200&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Frxswift_sync_09.6274d9ff.png&amp;w=1200&amp;q=75"></p>
<p><em>NsRecursiveLock</em></p>
<h3>2. ⚠️ Reentrancy anomaly was detected</h3>
<p>Nếu chúng ta đảm bảo các lời gọi tới Observer side một Subject luôn trên cùng một Thread,
nhưng vẫn có thể gặp lỗi <code class="notranslate" translate="no">⚠️ Reentrancy anomaly was detected</code>. Lỗi này hay gặp khi chúng ta gọi các Observer side của một Subject,
bên trong chính Observer của Subject.</p>
<p>✍️ Hãy lấy ví dụ gọi <code class="notranslate" translate="no">onCompleted</code> bên trong <code class="notranslate" translate="no">onNext</code> closure.</p>
<p align="center"><img alt="Reentrancy anomaly" loading="lazy" width="600" height="512" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Frxswift_sync_02.7b4978e2.png&amp;w=640&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Frxswift_sync_02.7b4978e2.png&amp;w=1200&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Frxswift_sync_02.7b4978e2.png&amp;w=1200&amp;q=75"></p>
<blockquote>
<p>❌ Lỗi này xảu ra khi <code class="notranslate" translate="no">onNext(2)</code> đang được delivered, và <code class="notranslate" translate="no">onCompleted</code> được gọi trong khi
đang trong quá trình delivery event 2.</p>
</blockquote>
<p>🧐 Tương tự như <code class="notranslate" translate="no">Synchronization anomaly</code>, RxSwift sẽ detect được chúng ta đang gọi 1 method của Observer side bên trong chính Observer của Subject,
và sẽ log ra lỗi <code class="notranslate" translate="no">⚠️ Reentrancy anomaly was detected</code> (hoặc crash nếu chúng ta enable flag <code class="notranslate" translate="no">FATAL_REENTRANCY</code> thông qua <code class="notranslate" translate="no">fatalError</code>).</p>
<p>✅ Cách fix đơn giản nhất là tránh gọi các method của Observer side bên trong chính Observer của Subject.
Hãy sử dụng các filtering operators như <code class="notranslate" translate="no">filter</code>, <code class="notranslate" translate="no">take</code>, <code class="notranslate" translate="no">skip</code>, <code class="notranslate" translate="no">distinctUntilChanged</code>, <code class="notranslate" translate="no">takeWhile</code>, <code class="notranslate" translate="no">takeUntil</code>, ...
để filter các event không mong muốn. <strong>Hãy reactive thay vì imperative</strong>.</p>
<br>
<br>
<hr>
<blockquote>
<p>They [Subjects] are the "mutable variables" of the Rx world and in most cases you do not need them.
Typically a solution with <code class="notranslate" translate="no">Create</code> or the other <code class="notranslate" translate="no">operators</code> allows you to just wire up continuations without adding extra state.
Stated slightly differently, it is good practice to minimize the number of objects that hold on to subscribers, you just want to pass them through.
(Erik Meijer - Rx.Net inventor).</p>
</blockquote>
<hr>
<br>
<br>
<h3>3. Tìm hiểu cách RxSwift detect các lỗi trên</h3>
<p>Đầu tiên, hãy xem source của <code class="notranslate" translate="no">PublishSubject.swift</code>.
<code class="notranslate" translate="no">PublishSubject</code> conforms <code class="notranslate" translate="no">ObserverType</code> protocol,
<code class="notranslate" translate="no">ObserverType</code> có một số extension <code class="notranslate" translate="no">onNext</code>, <code class="notranslate" translate="no">onError</code>, <code class="notranslate" translate="no">onCompleted</code> forward tới <code class="notranslate" translate="no">on(_ event: Event&lt;Int&gt;)</code>.</p>
<p align="center"><img alt="PublishSubject ObserverType" loading="lazy" width="600" height="652" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Frxswift_sync_10.4067b0a2.png&amp;w=640&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Frxswift_sync_10.4067b0a2.png&amp;w=1200&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Frxswift_sync_10.4067b0a2.png&amp;w=1200&amp;q=75"></p>
<p>Hãy xem implementation của <code class="notranslate" translate="no">on(_ event: Event&lt;Int&gt;)</code> trong <code class="notranslate" translate="no">PublishSubject.swift</code>.
Khi flag <code class="notranslate" translate="no">DEBUG</code> được enable, RxSwift sẽ dùng <code class="notranslate" translate="no">SynchronizationTracker</code> để track lúc <em>bắt đầu việc dispatch event</em>
(dòng code <code class="notranslate" translate="no">self.synchronizationTracker.register(synchronizationErrorMessage: .default)</code>)
và track lúc <em>kết thúc</em> (dòng code <code class="notranslate" translate="no">defer { self.synchronizationTracker.unregister() }</code>).</p>
<p align="center"><img alt="SynchronizationTracker register" loading="lazy" width="600" height="357" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Frxswift_sync_03.e8102c57.png&amp;w=640&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Frxswift_sync_03.e8102c57.png&amp;w=1200&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Frxswift_sync_03.e8102c57.png&amp;w=1200&amp;q=75"></p>
<p><code class="notranslate" translate="no">SynchronizationTracker</code> chứa một Dictionary <code class="notranslate" translate="no">var threads = [UnsafeMutableRawPointer: Int]()</code> với key là con trỏ tới <code class="notranslate" translate="no">Thread</code>,
value là số lượng lời gọi <code class="notranslate" translate="no">on(_ event: Event&lt;Int&gt;)</code> đang được thực thi (in-progress) trên Thread tương ứng.</p>
<p>Bên trong <code class="notranslate" translate="no">register</code>, chúng ta sẽ tăng value lên 1 cho key là con trỏ tới Thread hiện tại.
Nếu <code class="notranslate" translate="no">count &gt; 1</code>, tức là có nhiều hơn 1 lời gọi <code class="notranslate" translate="no">on(_ event: Event&lt;Int&gt;)</code> đang được thực thi (in-progress) trên Thread hiện tại,
và đang bị overlap lên nhau (Reentrancy anomaly).
Lúc đó, RxSwift sẽ log ra lỗi <code class="notranslate" translate="no">⚠️ Reentrancy anomaly was detected</code> hoặc crash.</p>
<p align="center"><img alt="SynchronizationTracker threads" loading="lazy" width="600" height="511" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Frxswift_sync_04.8e139edf.png&amp;w=640&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Frxswift_sync_04.8e139edf.png&amp;w=1200&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Frxswift_sync_04.8e139edf.png&amp;w=1200&amp;q=75"></p>
<p>Sau đó, check số lượng Threads đang trong trạng thái delivering.
Nếu số lượng threads đang thực thi việc delivery event lớn hơn một,
tức là có nhiều hơn 1 Thread đang delivery event đồng thời (Synchronization anomaly).
Lúc đó, RxSwift sẽ log ra lỗi <code class="notranslate" translate="no">⚠️ Synchronization anomaly was detected</code> hoặc crash.</p>
<p align="center"><img alt="SynchronizationTracker synchronization" loading="lazy" width="600" height="512" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Frxswift_sync_05.4dc16d5c.png&amp;w=640&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Frxswift_sync_05.4dc16d5c.png&amp;w=1200&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Frxswift_sync_05.4dc16d5c.png&amp;w=1200&amp;q=75"></p>
<p>Cuối cùng, sau khi delivery event, hàm <code class="notranslate" translate="no">unregister</code> được gọi để giảm số lượng value đi 1 cho key là con trỏ tới Thread hiện tại.
Nếu value về 0, tức là không còn lời gọi <code class="notranslate" translate="no">on(_ event: Event&lt;Int&gt;)</code> nào đang được thực thi trên Thread hiện tại,
chúng ta sẽ remove key đó ra khỏi Dictionary <code class="notranslate" translate="no">threads</code>.</p>
<p>Logic đơn giản như vậy thôi 🥰🥰.
Các bạn có thể tìm hiểu thêm trong source code của RxSwift <a href="https://github.com/ReactiveX/RxSwift/blob/95917a57a58734cd7b747361add398906e8b255c/RxSwift/Rx.swift#L70">Rx.swift</a>
và <a href="https://github.com/ReactiveX/RxSwift/blob/95917a57a58734cd7b747361add398906e8b255c/RxSwift/Subjects/PublishSubject.swift#L56">PublishSubject.swift</a>.</p>
<hr>
<p>Follow tôi, chúng tôi <a href="https://rx-mobile-team.github.io/profile/">https://rx-mobile-team.github.io/profile/</a> để có thêm nhiều kiến thức về lập trình, không chỉ giới hạn
ở Mobile (Android/iOS/Flutter) mà có cả Functional Programming, Reactive Programming, Data Structures, Algorithms, ...
Những kiến thức chia sẻ ở đây, rất ít các Senior Dev và vân..vân.. chia sẻ cho các bạn đâu.</p>]]></content:encoded>
            <author>hoc081098@gmail.com (Petrus Nguyễn Thái Học (hoc081098))</author>
        </item>
    </channel>
</rss>