<?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://hoc081098-portfolio-website.vercel.app</link>
        <description>Articles on Android, Flutter, iOS, reactive programming, and clean architecture.</description>
        <lastBuildDate>Sat, 02 May 2026 17:04:30 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://hoc081098-portfolio-website.vercel.app/favicon.ico</url>
            <link>https://hoc081098-portfolio-website.vercel.app</link>
        </image>
        <copyright>All rights reserved 2026</copyright>
        <item>
            <title><![CDATA[💠 Union types trong C# (bản Tiếng Việt)]]></title>
            <link>https://hoc081098-portfolio-website.vercel.app/articles/csharp-union-types-vi</link>
            <guid>https://hoc081098-portfolio-website.vercel.app/articles/csharp-union-types-vi</guid>
            <pubDate>Sun, 05 Apr 2026 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p><a href="https://hits.sh/portfolio.hoc081098.dev/articles/csharp-union-types-vi/"><img src="https://hits.sh/portfolio.hoc081098.dev/articles/csharp-union-types-vi.svg" alt="Hits"></a></p>
<blockquote>
<p>Estimated reading time: 4 minutes</p>
</blockquote>
<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>UnionAttribute</code> và <code>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>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="language-csharp"><code class="language-csharp"><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="language-csharp"><code class="language-csharp"><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>Pet</code> chỉ là <strong>một cái hộp</strong>, hoặc chứa <code>Cat</code>, hoặc chứa <code>Dog</code></li>
<li><code>Pet</code> được gọi là <strong>union type</strong></li>
<li><code>Cat</code> và <code>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="language-csharp"><code class="language-csharp"><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>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>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>.Value</code>, rồi apply pattern lên <code>Value</code> đó
<em>(ngoại trừ trường hợp dùng <code>var</code> hoặc <code>_</code>)</em>.</p>
</li>
</ul>
<p>Ví dụ:</p>
<pre class="language-csharp"><code class="language-csharp"><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="language-csharp"><code class="language-csharp"><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>Pet</code> là một <em>union type</em>, nên nó sẽ biết <code>switch expression</code> ở trên đã được <strong>exhaustive</strong> rồi.</p>
<h4>💠 2.2.2. Ngoại lệ <code>var</code> / <code>_</code></h4>
<p>Ngoại lệ là <code>var</code> hoặc <code>_</code> pattern. Khi đó, pattern sẽ được apply vào <strong>chính <code>Pet</code> value</strong>, chứ không phải <code>Value</code> của <code>Pet</code>.</p>
<pre class="language-csharp"><code class="language-csharp"><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>null</code></h4>
<p>Khi check 1 biến kiểu nullable union type là <code>null</code> hay không,
thì việc check <code>null</code> không chỉ apply cho union type value, mà cả <code>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="language-csharp"><code class="language-csharp"><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="language-csharp"><code class="language-csharp"><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="language-csharp"><code class="language-csharp"><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>false</code>, vì nó bị <em>lowered</em> thành:</p>
<pre class="language-csharp"><code class="language-csharp">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>Value</code> luôn là <code>Dog</code> hoặc <code>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>GetPet()</code> trả về kiểu <code>object</code>, thì câu chuyện đổi hẳn.</p>
<pre class="language-csharp"><code class="language-csharp"><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>GetPet() is Pet</code> sẽ eval thành <code>true</code> <strong>nếu <code>GetPet()</code> trả về một instance của <code>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>object</code>, không phải <code>Pet</code>.</li>
<li>Do đó, <code>GetPet() is Pet</code> sẽ là <code>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>.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="language-csharp"><code class="language-csharp"><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>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="language-csharp"><code class="language-csharp"><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://hoc081098-portfolio-website.vercel.app/articles/csharp-union-types</link>
            <guid>https://hoc081098-portfolio-website.vercel.app/articles/csharp-union-types</guid>
            <pubDate>Sun, 05 Apr 2026 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p><a href="https://hits.sh/portfolio.hoc081098.dev/articles/csharp-union-types/"><img src="https://hits.sh/portfolio.hoc081098.dev/articles/csharp-union-types.svg" alt="Hits"></a></p>
<blockquote>
<p>Estimated reading time: 4 minutes</p>
</blockquote>
<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>UnionAttribute</code> and <code>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>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="language-csharp"><code class="language-csharp"><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="language-csharp"><code class="language-csharp"><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>Pet</code> is just <strong>a box</strong> that holds either a <code>Cat</code> or a <code>Dog</code></li>
<li><code>Pet</code> is called the <strong>union type</strong></li>
<li><code>Cat</code> and <code>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="language-csharp"><code class="language-csharp"><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>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>Value</code> inside the union type</strong>.</p>
<p>In other words, the union is <strong>implicitly unwrapped</strong> via <code>.Value</code>, and the pattern is applied to that <code>Value</code>
<em>(except when using <code>var</code> or <code>_</code>)</em>.</p>
</li>
</ul>
<p>Example:</p>
<pre class="language-csharp"><code class="language-csharp"><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="language-csharp"><code class="language-csharp"><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>Pet</code> is a <em>union type</em>, it knows the <code>switch expression</code> above is already <strong>exhaustive</strong>.</p>
<h4>💠 2.2.2. Exception: <code>var</code> / <code>_</code></h4>
<p>The <code>var</code> or <code>_</code> patterns are exceptions. In these cases, the pattern is applied to <strong>the <code>Pet</code> value itself</strong>, not its <code>Value</code>.</p>
<pre class="language-csharp"><code class="language-csharp"><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>null</code>,
the <code>null</code> check applies not only to the union value itself but also to the <code>Value</code> inside it.
(This still follows the Union behavior above — when the compile-time type is a union type.)</p>
<pre class="language-csharp"><code class="language-csharp"><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="language-csharp"><code class="language-csharp"><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="language-csharp"><code class="language-csharp"><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>false</code>, because it is <em>lowered</em> to:</p>
<pre class="language-csharp"><code class="language-csharp">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>Value</code> is always <code>Dog</code> or <code>Cat</code> 😂.</p>
<hr>
<h2>💠 3. A Common Source of Confusion: Changing the Compile-Time Type Changes the Meaning</h2>
<p>If <code>GetPet()</code> returns <code>object</code>, the story changes entirely.</p>
<pre class="language-csharp"><code class="language-csharp"><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>GetPet() is Pet</code> evaluates to <code>true</code> <strong>if <code>GetPet()</code> returns an instance of <code>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>object</code>, not <code>Pet</code>.</li>
<li>Therefore, <code>GetPet() is Pet</code> is <code>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>.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="language-csharp"><code class="language-csharp"><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>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="language-csharp"><code class="language-csharp"><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://hoc081098-portfolio-website.vercel.app/articles/grouping-in-kotlin</link>
            <guid>https://hoc081098-portfolio-website.vercel.app/articles/grouping-in-kotlin</guid>
            <pubDate>Sun, 28 May 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p><a href="https://hits.sh/portfolio.hoc081098.dev/articles/grouping-in-kotlin/"><img src="https://hits.sh/portfolio.hoc081098.dev/articles/grouping-in-kotlin.svg" alt="Hits"></a></p>
<h2>Author: <a href="https://github.com/hoc081098">Petrus Nguyễn Thái Học</a></h2>
<ul>
<li><em>Tags</em>: #kotlin, #grouping, #groupingBy, #groupBy, #reduce, #lazy-evaluation, #functional-programming, #hoc081098, #rx_mobile_team,
#kotlindev #androiddev</li>
<li><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></li>
</ul>
<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>id</code>: mã sinh viên.</li>
<li><code>name</code>: tên sinh viên.</li>
<li><code>classId</code>: mã lớp.</li>
<li><code>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="language-kotlin"><code class="language-kotlin"><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="language-kotlin"><code class="language-kotlin"><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>groupBy</code> và <code>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="language-kotlin"><code class="language-kotlin"><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>groupingBy</code> và <code>reduce</code></h2>
<pre class="language-kotlin"><code class="language-kotlin"><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>groupingBy</code> sẽ tạo và return 1 object <code>Grouping</code> để lưu trữ <code>Iterator</code> và <code>Key Selector</code> để thực hiện việc <code>reduce</code>/<code>aggregate</code> sau đó.</p>
<pre class="language-kotlin"><code class="language-kotlin"><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>Grouping</code> không thực hiện việc duyệt, <code>reduce</code>/<code>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>aggregate</code> sau này.
Đó là cơ chế <strong>LAZY</strong>, tương tự như <code>Sequence</code> của Koltin, <code>Stream</code> của Java, <code>Observable</code> của RxJava, <code>Flow</code> của Kotlin Coroutines, ...</p>
</li>
<li>
<p>Sau đó, <code>reduceTo</code> sẽ duyệt qua <code>Grouping</code> object, và thực hiện việc <code>reduce</code>/<code>aggregate</code> trên từng <code>Group</code> xác định bởi <code>Key Selector</code>.
Hàm <code>reduceTo</code> trên <code>Grouping</code> có cơ chế khá giống trên <code>Iterable</code>, nhưng nó không thực thi <code>operation</code> trên toàn bộ <code>Iterable</code>,
mà nó thực thi <code>operation</code> trên từng <code>Group</code> xác định bởi <code>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="language-kotlin"><code class="language-kotlin"><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://hoc081098-portfolio-website.vercel.app/articles/jetpack-compose-dp-class</link>
            <guid>https://hoc081098-portfolio-website.vercel.app/articles/jetpack-compose-dp-class</guid>
            <pubDate>Sat, 16 Sep 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p><a href="https://hits.sh/portfolio.hoc081098.dev/articles/jetpack-compose-dp-class/"><img src="https://hits.sh/portfolio.hoc081098.dev/articles/jetpack-compose-dp-class.svg" alt="Hits"></a></p>
<h2>Why do @Composable functions in Jetpack Compose usually use <code>Dp.Unspecified</code> as default value? Why not use null? The optimization of Jetpack Compose under the hood</h2>
<ul>
<li>Author: <a href="https://github.com/hoc081098">Petrus Nguyễn Thái Học</a></li>
<li><em>Published on Sep 16, 2023</em></li>
<li><em>5 minutes read so far</em></li>
<li><em>Tags</em>: #hoc081098, #rx_mobile_team, #kotlindev #androiddev, #rxandroid, #jetpack_compose, #value_class</li>
</ul>
<p>When using Jetpack Compose, we use <code>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>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>
<h3>I. <code>Dp</code> class</h3>
<h4>I.1. <code>Dp</code> class declaration</h4>
<p>At the moment, <code>Dp</code> class is defined as follows:</p>
<pre class="language-kotlin"><code class="language-kotlin"><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>Dp</code> class is declared as <code>inline value class</code> wrapping a <code>Float</code> value.
When compiling, the compiler will try to replace all <code>Dp</code> types with <code>Float</code> types (<code>float</code> in Java) as much as
possible.
This reduces memory usage and improves performance since heap allocation is eliminated,
and <code>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>
<h4>I.2. Example ✍️</h4>
<p>Let's create a simple example to see how the compiler optimizes <code>Dp</code> class, I create a <code>@Composable fun MyComposable1</code>
accepting a <code>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>Dp</code> types with <code>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>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>
<h3>II. Why do @Composable functions in Jetpack Compose usually use <code>Dp.Unspecified</code> as default value? Why not use null?</h3>
<h3>II.1. Why not use null?</h3>
<p>After looking at the <code>Dp</code> class, we can understand why <code>Dp</code> is declared as <code>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>Inline value class</code> and <code>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>null</code> as default value, we must declare the type as <code>Dp?</code> (nullable type).
Since <code>Dp?</code> is a nullable type, compiler cannot use <code>Float</code> (aka <code>float</code> in Java) type to replace <code>Dp?</code> type.
Instead, <code>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>Dp?</code> is a reference type, not a primitive type, it requires <strong>heap allocation</strong>.
Imagine we use <code>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>
<h4>II.2. Demonstration example ✍️</h4>
<p>Let's create a simple example using <code>Dp?</code> type, we create a <code>@Composable fun MyComposable2</code> accepting a <code>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>Dp</code> type is not replaced with <code>Float</code> type, it is preserved.
Inside <code>UseMyComposable2</code>, <code>Dp.box-impl(Dp.constructor-impl((float)$this$dp$iv))</code> is used
to box a <code>Float</code> value to a <code>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>null</code> as default value</strong>.
<code>Dp.Unspecified</code> is a better choice ✅, it wraps a <code>Float.NaN</code> value.
Sometimes, we can use <code>Dp.Hairline</code> (aka <code>0.dp</code>) as default value, depending on your use case.</p>
<h4>II.3. Bonus ✅</h4>
<ul>
<li>How about elvis operator <code>?:</code>?</li>
<li>How about <code>== null</code>?</li>
</ul>
<p>Jetpack Compose has replacements, they are <code>Dp.takeOrElse(block: () -&gt; Dp): Dp</code> and <code>Dp.isUnspecified: Boolean</code>.
<code>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[Rate limiting strategies for Nginx, API Gateway, and service layers]]></title>
            <link>https://hoc081098-portfolio-website.vercel.app/articles/rate-limiting-strategies</link>
            <guid>https://hoc081098-portfolio-website.vercel.app/articles/rate-limiting-strategies</guid>
            <pubDate>Sat, 14 Feb 2026 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p><a href="https://hits.sh/portfolio.hoc081098.dev/articles/rate-limiting-strategies/"><img src="https://hits.sh/portfolio.hoc081098.dev/articles/rate-limiting-strategies.svg" alt="Hits"></a></p>
<blockquote>
<p>Estimated reading time: 7 minutes</p>
</blockquote>
<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><code>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="language-nginx"><code class="language-nginx"><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>limit_req_zone</code> directive trong <code>http</code> block với</p>
<ul>
<li><code>$binary_remote_addr</code>: sử dụng IP address của client trong dạng nhị phân để làm KEY.</li>
<li><code>zone=mylimit:10m</code>: tạo 1 zone tên là "mylimit" có size <code>10MB</code> memory (có thể lưu trữ trạng thái của khoảng 160,000 địa chỉ IP)</li>
<li><code>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>limit_req</code> directive trong <code>location</code> block để áp dụng limit đã định nghĩa ở trên.</p>
<ul>
<li><code>zone=mylimit</code>: chỉ định zone đã tạo ở trên</li>
<li><code>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>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>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>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>rate</code> lẫn <code>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>Program.cs</code></p>
<pre class="language-csharp"><code class="language-csharp"><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>appsettings.json</code> - Apply <code>customPolicy</code> rate limit policy cho route1:</p>
<pre class="language-json"><code class="language-json"><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://hoc081098-portfolio-website.vercel.app/articles/rx-subject-sync</link>
            <guid>https://hoc081098-portfolio-website.vercel.app/articles/rx-subject-sync</guid>
            <pubDate>Sat, 17 Jun 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p><a href="https://hits.sh/portfolio.hoc081098.dev/articles/rx-subject-sync/"><img src="https://hits.sh/portfolio.hoc081098.dev/articles/rx-subject-sync.svg" alt="Hits"></a></p>
<h2>Author: <a href="https://github.com/hoc081098">Petrus Nguyễn Thi Học</a></h2>
<ul>
<li><em>Tags</em>: #functional-programming, #hoc081098, #rx_mobile_team, #kotlindev #androiddev, #iosdev, #rxswift, #rxjava, #rxkotlin
#functional_reactive_programming, #reactive_programming, #reactive_extensions, #reactive_programming, #reactive_extensions, #rxjava2, #rxjava3, #rxswift, #rxkotlin, #rxandroid, #rxmobile</li>
</ul>
<h2>I. Serially rule 😇</h2>
<p><code>Observable</code> trong <code>ReactiveX</code> phải thuân thủ quy tắc <code>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>onNext(1)</code>, <code>onNext(2)</code>, <code>onNext(3)</code> thì 1 subscriber có thể nhận
<code>onNext(1)</code> ở thread A, <code>onNext(2)</code> ở thread B, <code>onNext(3)</code> ở thread A,
nhưng nó không được phép nhận <code>onNext(2)</code> trong khi <code>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>Subject</code> không phải là thread-safe ở phía Observer.
Nếu chúng ta invoke <code>onNext</code>, <code>onError</code>, <code>onCompleted</code> trên <code>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>Subject</code> không đảm bảo quy tắc <code>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>SerializedSubject</code></a>.
Chỉ cần gọi <code>toSerialized()</code> trên 1 Subject bất kì là chúng ta đã có được một <code>SerializedSubject</code>. <code>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>AppendOnlyLinkedArrayList</code>
được synchronized bởi chính <code>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>PublishSubject</code>, <code>BehaviorSubject</code>, <code>ReplaySubject</code>, <code>AsyncSubject</code> đều không thread-safe.</p>
<p>✍️ Hãy lấy ví dụ với <code>PublishSubject</code>, gọi <code>onNext</code> trên <code>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>onNext(1)</code> được send từ thread của <code>queue-1</code>, <code>onNext(2)</code> event sau lại được
send từ thread của <code>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>⚠️ Synchronization anomaly was detected</code>.
Nếu chúng ta enable flag <code>FATAL_SYNCHRONIZATION</code>, thì RxSwift sẽ crash app thông qua <code>fatalError</code>.</p>
<p>✅ Cách fix đơn giản nhất là tạo một <code>Serial DispatchQueue</code>, và đưa các lời gọi tới <code>PublishSubject</code> vào trong DispatchQueue đó.
Hoặc sử dụng một <code>NsRecursiveLock</code> để đảm bảo các lời gọi tới Observer side của <code>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>⚠️ 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>onCompleted</code> bên trong <code>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>onNext(2)</code> đang được delivered, và <code>onCompleted</code> được gọi trong khi
đang trong quá trình delivery event 2.</p>
</blockquote>
<p>🧐 Tương tự như <code>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>⚠️ Reentrancy anomaly was detected</code> (hoặc crash nếu chúng ta enable flag <code>FATAL_REENTRANCY</code> thông qua <code>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>filter</code>, <code>take</code>, <code>skip</code>, <code>distinctUntilChanged</code>, <code>takeWhile</code>, <code>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>Create</code> or the other <code>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>PublishSubject.swift</code>.
<code>PublishSubject</code> conforms <code>ObserverType</code> protocol,
<code>ObserverType</code> có một số extension <code>onNext</code>, <code>onError</code>, <code>onCompleted</code> forward tới <code>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>on(_ event: Event&lt;Int&gt;)</code> trong <code>PublishSubject.swift</code>.
Khi flag <code>DEBUG</code> được enable, RxSwift sẽ dùng <code>SynchronizationTracker</code> để track lúc <em>bắt đầu việc dispatch event</em>
(dòng code <code>self.synchronizationTracker.register(synchronizationErrorMessage: .default)</code>)
và track lúc <em>kết thúc</em> (dòng code <code>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>SynchronizationTracker</code> chứa một Dictionary <code>var threads = [UnsafeMutableRawPointer: Int]()</code> với key là con trỏ tới <code>Thread</code>,
value là số lượng lời gọi <code>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>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>count &gt; 1</code>, tức là có nhiều hơn 1 lời gọi <code>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>⚠️ 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>⚠️ 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>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>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>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>