<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Gap's Tech Blog]]></title><description><![CDATA[Writing is a reflection gym. Here is my gym room]]></description><link>https://intl.gaplo.tech/</link><image><url>https://intl.gaplo.tech/favicon.png</url><title>Gap&apos;s Tech Blog</title><link>https://intl.gaplo.tech/</link></image><generator>Ghost 5.58</generator><lastBuildDate>Fri, 08 May 2026 15:49:58 GMT</lastBuildDate><atom:link href="https://intl.gaplo.tech/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[My top concern when engineer spend 90%+ time reviewing AI-generated codes]]></title><description><![CDATA[My top concern when application engineers are going to spend 90%+ time reviewing AI-generated codes in the upcoming years -- the human reviewing speed.]]></description><link>https://intl.gaplo.tech/my-top-concern-when-engineer-spend-90-time-reviewing-ai-generated-codes/</link><guid isPermaLink="false">69abd6f0fdcf1206f0010575</guid><category><![CDATA[kotlin]]></category><category><![CDATA[GenAI]]></category><category><![CDATA[Python]]></category><dc:creator><![CDATA[Gap]]></dc:creator><pubDate>Sun, 02 Nov 2025 08:00:00 GMT</pubDate><media:content url="https://intl.gaplo.tech/content/images/2026/03/kotlin-code-review.png" medium="image"/><content:encoded><![CDATA[<img src="https://intl.gaplo.tech/content/images/2026/03/kotlin-code-review.png" alt="My top concern when engineer spend 90%+ time reviewing AI-generated codes"><p>My top concern when application engineers are going to spend 90%+ time reviewing AI-generated codes in the upcoming years -- <strong>the human reviewing speed</strong>.</p><p>In the real world, someone in the team must take accountability for the correctness and performance of the source code. It doesn&apos;t make sense for anyone to make a disclaimer like</p><blockquote>This system is generated by AI and might contain errors.</blockquote><p>If 99%+ of source code will eventually be generated by LLMs, the human review process becomes critical.</p><h2 id="underestimated-the-effect-of-choosing-programming-languages">Underestimated the effect of choosing programming languages</h2><p>I have 8 years of application development experience using strong, statically typed programming languages, including Java, Scala, Swift, Javascript, Typescript, and Kotlin, from before the GenAI era.</p><p>For the recent two years, the latest GenAI libraries usually released in Python first. Thus, I have been heavily using LLMs to generate Python code while building and exploring GenAI PoC with my customers across different frameworks like <a href="https://github.com/langchain-ai/langchain?ref=intl.gaplo.tech">LangChain</a>, <a href="https://github.com/langchain-ai/langgraph?ref=intl.gaplo.tech">LangGraph</a>, <a href="https://github.com/google/adk-python?ref=intl.gaplo.tech">Google ADK</a>, <a href="https://github.com/strands-agents/sdk-python?ref=intl.gaplo.tech">Strands Agent</a>, and more.</p><p>The longer I read and write Python, the more I miss statically typed programming languages, especially Kotlin and its compiler.</p><p>Before the GenAI era, Kotlin has always been my go-to programming language because of its elegant design. Its rich language features enable me to express myself through different forms of abstraction very efficiently. The static typing and type inference help me navigate others&apos; source code effectively.</p><p>Now, in GenAI era, LLMs can generate high quality source code for many popular programming languages.<strong> </strong>The underestimated part is to<strong> leverage compilers in Agent tool to guarantee their output has zero type mismatches or syntax errors at compile time</strong>.</p><h2 id="code-reviewing-efficiency">Code reviewing efficiency</h2><p>In my 8 years development experience, I was 99.9% of the time able to navigate deeply Java/Kotlin open-source libraries&apos; source code in my IDE and trace the underlying low-level implementations and read source code documentation. This significantly helped build confidence and learn fast when using new libraries.</p><p>While with Python, it is less than 50%. It is because some Python implementations use dynamic typing with no type hint or with Union type hint makes it difficult to navigate deeply.</p><p>See the common Result type implementation:</p><blockquote>Can you spot error during the review?</blockquote><pre><code class="language-python">from typing import Union

class SuccessResultA:
    a: int = 1

class FailureResultB:
    b: str = &quot;1&quot;

def call(x: int) -&gt; Union[SuccessResultA, FailureResultB]:
    if x%2 == 1:
        return SuccessResultA()
    else:
        return FailureResultB()

result = call(0)

print(result.b)</code></pre><p>Let&apos;s run it</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://intl.gaplo.tech/content/images/2026/03/python-dynamic-typing.png" class="kg-image" alt="My top concern when engineer spend 90%+ time reviewing AI-generated codes" loading="lazy" width="978" height="1000" srcset="https://intl.gaplo.tech/content/images/size/w600/2026/03/python-dynamic-typing.png 600w, https://intl.gaplo.tech/content/images/2026/03/python-dynamic-typing.png 978w" sizes="(min-width: 720px) 720px"><figcaption>Python - no error at runtime</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://intl.gaplo.tech/content/images/2026/03/python-typing-error.png" class="kg-image" alt="My top concern when engineer spend 90%+ time reviewing AI-generated codes" loading="lazy" width="843" height="1000" srcset="https://intl.gaplo.tech/content/images/size/w600/2026/03/python-typing-error.png 600w, https://intl.gaplo.tech/content/images/2026/03/python-typing-error.png 843w" sizes="(min-width: 720px) 720px"><figcaption>Python - type error at runtime</figcaption></figure><p>As the result type is produced dynamically base on runtime, sometimes the program will works.</p><blockquote>How would you review the source code effectively if LLM generate source code with 4 - 10 abstraction layers of union types in Python build from ground up?</blockquote><p>On the contrast, the similar abstraction to Python Union type in Kotlin is sealed class, it is static typing and checked by complier at compile time.</p><p>The best part of compile time error is that, it has no runtime dependencies or side effect.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://intl.gaplo.tech/content/images/2026/03/kotin-static-typing.png" class="kg-image" alt="My top concern when engineer spend 90%+ time reviewing AI-generated codes" loading="lazy" width="1162" height="848" srcset="https://intl.gaplo.tech/content/images/size/w600/2026/03/kotin-static-typing.png 600w, https://intl.gaplo.tech/content/images/size/w1000/2026/03/kotin-static-typing.png 1000w, https://intl.gaplo.tech/content/images/2026/03/kotin-static-typing.png 1162w" sizes="(min-width: 720px) 720px"><figcaption>Kotlin - compile time error</figcaption></figure><p>When I am reviewing the source code, I would check and navigate the types. The Kotlin smart cast annotation really shine in this area.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://intl.gaplo.tech/content/images/2026/03/kotin-smart-cast.png" class="kg-image" alt="My top concern when engineer spend 90%+ time reviewing AI-generated codes" loading="lazy" width="1488" height="947" srcset="https://intl.gaplo.tech/content/images/size/w600/2026/03/kotin-smart-cast.png 600w, https://intl.gaplo.tech/content/images/size/w1000/2026/03/kotin-smart-cast.png 1000w, https://intl.gaplo.tech/content/images/2026/03/kotin-smart-cast.png 1488w" sizes="(min-width: 720px) 720px"><figcaption>Kotlin - Smart cast after matching</figcaption></figure><p>Roughly speaking, when I review the same AI generated source code in Kotlin, the static typing, language conciseness and complier reduces 80% mental cognitive<strong> </strong>load. The complie-time type errors help to reduce 80% Agentic-back-and-forth time comparing to Python.</p><p>Although my current job role is unlikely to involve building complex and long-lived production systems, I hope newcomers to this industry aware the choice of programming languages.</p><blockquote>The technology stacks of building throwable GenAI PoCs are completely different from building long-lived applications.</blockquote><p>If you are new to this application development industry, I highly recommend you to spend some effort to deep dive into different programming languages with AI, especially on the strong and static typed programming language.</p><p>The ultimate goal is to take accountability and review AI generated codes as efficient as possible.</p><h2 id="jupyter-python-notebook-is-great-now-available-in-kotlinkotlin-notebook">Jupyter Python Notebook is great, now available in Kotlin - Kotlin Notebook</h2><p>In terms of learning a new language, my first &quot;wow&quot; moment was experiencing Python Jupyter notebooks. These notebooks helped me tremendously in picking up new technologies and allowed for rapid experimentation.</p><p>When I discovered that <a href="https://blog.jetbrains.com/idea/2025/04/kotlin-notebook-arrives-in-intellij-idea/?ref=intl.gaplo.tech">Kotlin Notebook had become freely available</a> and JetBrains is building the <a href="https://blog.jetbrains.com/ai/2025/10/koog-a2a-building-connected-ai-agents-in-kotlin/?ref=intl.gaplo.tech">latest Kotlin-native AI agent framework (Koog)</a>, I started an open-source project to consolidate my Kotlin experiments and share them online.</p><p>GitHub repository: <a href="https://github.com/gaplo917/awesome-kotlin-notebook?ref=intl.gaplo.tech">https://github.com/gaplo917/awesome-kotlin-notebook</a></p><p>I am trying to build a multi-agent system using A2A protocol with strong static typing in Kotlin running on my M4Max Macbook Pro. It is for my own learning about A2A and the experience is transferrable to Python as well.</p><p>Notes: I don&apos;t use LLM generate ideas as it is the interesting part of being a human. I use LLM to work on my ideas instead.</p>]]></content:encoded></item><item><title><![CDATA[Kotlin K2 Compiler will improve your expression design]]></title><description><![CDATA[Kotlin 2.0 is finally arrived and become stable. The most interesting upgrade to Kotlin 2.0 is the K2 compiler, it helps your project build faster and allow you to write smarter codes.]]></description><link>https://intl.gaplo.tech/kotlin-k2-complier-will-improve-your-expression/</link><guid isPermaLink="false">6735c328fdcf1206f0010480</guid><category><![CDATA[kotlin]]></category><category><![CDATA[k2-compiler]]></category><dc:creator><![CDATA[Gap]]></dc:creator><pubDate>Sat, 01 Jun 2024 09:55:00 GMT</pubDate><media:content url="https://intl.gaplo.tech/content/images/2024/11/kotlin-playground-1.png" medium="image"/><content:encoded><![CDATA[<img src="https://intl.gaplo.tech/content/images/2024/11/kotlin-playground-1.png" alt="Kotlin K2 Compiler will improve your expression design"><p><a href="https://kotlinlang.org/docs/whatsnew20.html?ref=intl.gaplo.tech">Kotlin 2.0 is finally arrived</a> and become stable. The most interesting upgrade to Kotlin 2.0 is the K2 compiler, it helps your project build faster and allow you to write smarter codes.</p><p>In the <a href="https://blog.jetbrains.com/kotlin/2024/04/k2-compiler-performance-benchmarks-and-how-to-measure-them-on-your-projects/?ref=intl.gaplo.tech">recent JetBrain blog post</a>, it shows Kotlin 2.0.0 (K2) vs 1.9.23 performance gains:</p><ul><li>Clean build: 94% faster (57.7s &#x2192; 29.7s)</li><li>Initialization: 488% faster (0.126s &#x2192; 0.022s)</li><li>Analysis: 376% faster (0.581s &#x2192; 0.122s)</li></ul><p>Not only the performance is significant improved, the smart cast handling on K2 compiler also significantly improved. I consolidated five examples:</p><div class="kg-card kg-callout-card kg-callout-card-green"><div class="kg-callout-emoji">&#x1F4A1;</div><div class="kg-callout-text">You can test the following examples on <a href="https://play.kotlinlang.org/?ref=intl.gaplo.tech">https://play.kotlinlang.org/</a></div></div><figure class="kg-card kg-image-card"><img src="https://intl.gaplo.tech/content/images/2024/11/kotlin-playground.png" class="kg-image" alt="Kotlin K2 Compiler will improve your expression design" loading="lazy" width="1698" height="1538" srcset="https://intl.gaplo.tech/content/images/size/w600/2024/11/kotlin-playground.png 600w, https://intl.gaplo.tech/content/images/size/w1000/2024/11/kotlin-playground.png 1000w, https://intl.gaplo.tech/content/images/size/w1600/2024/11/kotlin-playground.png 1600w, https://intl.gaplo.tech/content/images/2024/11/kotlin-playground.png 1698w" sizes="(min-width: 720px) 720px"></figure><hr><h3 id="handling-generic-box-interface">Handling generic box interface</h3><pre><code class="language-kotlin">interface Box&lt;T&gt;
val &lt;X&gt; Box&lt;X&gt;.first: X get() = TODO()

fun foo(p: Box&lt;() -&gt; Unit&gt;) {
    // K1: compile error
    // K2: ok
    p.first()
    p.first.invoke()
}</code></pre><hr><h3 id="synthetic-data-flow-variables-can-carry-information-about-smart-casts">Synthetic data flow variables can carry information about smart-casts</h3><pre><code class="language-kotlin">fun test(foo: String?) {
    if (foo != null) {
        foo.length
    }
    
    val fooIsNonNull = foo != null
    if (fooIsNonNull) {
        // K1: compile error , unsafe call
        // K2: ok
        foo.length
    }
}</code></pre><hr><h3 id="smart-casts-inside-changing-closures-of-lambdas">Smart-casts inside changing closures of lambdas</h3><ul><li>In general, we don&apos;t know anything about the <code>inPlaceRun</code> function</li><li>The parameter <code>f</code> could be invoked later or just stored somewhere, and value could be changed after the lambda block</li></ul><figure class="kg-card kg-code-card"><pre><code class="language-kotlin">fun inPlaceRun(f: () &#x2192; Unit) {
    f()
}

fun foo() {
    var value: Int? = null
    inPlaceRun {
        // K1: compile error, smart cast to &apos;Int&apos; is impossible
        // K2: ok
        if (value != null) value++ else value = 0
    }
}</code></pre><figcaption>K1</figcaption></figure><p>Explicitly tell the compiler that the parameter <code>f</code> will be invoked only within the method and won&apos;t be stored anywhere else.</p><figure class="kg-card kg-code-card"><pre><code class="language-kotlin">@OptIn(ExperimentalContracts::class)
fun inPlaceRun(f: () &#x2192; Unit) {
    contract {
        callsInPlace(f, InvocationKind.UNKNOWN)
    }
    f()
}

fun foo() {
    var value: Int? = null
    inPlaceRun {
        // K2: smart cast to &apos;Int&apos;
        if (value != null) value++ else value = 0
    }
</code></pre><figcaption>K2</figcaption></figure><hr><h3 id="smart-casts-inside-closures-of-inline-lambdas">Smart-casts inside closures of inline lambdas</h3><ul><li>In K1, it&apos;s impossible to have a smart-cast because the lambda can be invoked later, even after <code>forEachIndexed</code> call</li></ul><pre><code class="language-kotlin">fun indexOfMax(a: IntArray): Int? {
    var maxI: Int? = null
    a.forEachIndexed { i, value -&gt;
        // K1: compile error, smart cast to &apos;Int&apos; is impossible,
        // because &apos;maxI&apos; is a local variable that is captured by a changing closure
        // K2: Ok
        if (maxI == null || a[maxI] &lt;= value) {
            maxI = i
        }
    }
    return maxI
}</code></pre><hr><h3 id="smart-casts-after-merge-to-a-common-supertype">Smart-casts after ||: merge to a common supertype</h3><p>K2 compiler now can recognize <code>||</code> case in the condition. You don&apos;t need to write switch case now.</p><pre><code class="language-kotlin">interface Status {
    fun signal()
}

interface Ok : Status
interface Postponed : Status
interface Declined : Status

fun foo(o: Any) {
    if (o is Postponed || o is Declined) {
        // K1: compiler error, o is inferred to Any
        // K2: o is inferred to Status
        o.signal()
    }
}</code></pre>]]></content:encoded></item><item><title><![CDATA[React Offload UI Thread Research]]></title><description><![CDATA[An I/O call is non-blocking on UI thread doesn't mean that it doesn't use the UI thread CPU time.]]></description><link>https://intl.gaplo.tech/react-offload-ui-thread-research/</link><guid isPermaLink="false">61c4518f1fe2271b4292fd38</guid><category><![CDATA[Research]]></category><category><![CDATA[javascript]]></category><category><![CDATA[React]]></category><dc:creator><![CDATA[Gap]]></dc:creator><pubDate>Wed, 23 Sep 2020 00:00:00 GMT</pubDate><media:content url="https://intl.gaplo.tech/content/images/2021/12/react-offload-ui-thread-w-border-2.png" medium="image"/><content:encoded><![CDATA[<div class="kg-card kg-callout-card kg-callout-card-blue"><div class="kg-callout-emoji">&#x1F37E;</div><div class="kg-callout-text">This article was published on <a href="https://www.patreon.com/gaplotech?ref=intl.gaplo.tech">Patreon members-only</a> in late Sep 2020. In 2022, I decided to bring it back to my blog and set it free and open for everyone in the world.<br><br>If you find this article bringing you new insight, <a href="https://www.patreon.com/gaplotech?ref=intl.gaplo.tech">please consider joining Patreon to show your support!</a></div></div><h2 id="foreword-beyond-javascript-development">Foreword: Beyond Javascript Development</h2><img src="https://intl.gaplo.tech/content/images/2021/12/react-offload-ui-thread-w-border-2.png" alt="React Offload UI Thread Research"><p>Before I have started React/Vue/Angular development, I have been working on building iOS and Android mobile applications for a few years that use MVVM architecture.</p><p>When developing a frontend application, we undoubtedly offload all kinds of I/O, computational tasks from a UI thread to prevent a UI thread from being too busy and becoming unresponsive. </p><blockquote class="kg-blockquote-alt">This rule doesn&apos;t apply to current web development.</blockquote><p>The current web development <strong>ONLY</strong> offloads the tasks to web worker when the application encounter performance issues but <strong>NOT</strong> by the tasks&apos; nature.</p><p>As we all know, web workers are designed to offload works from a UI thread, but why don&apos;t developers utilize it by default?</p><h2 id="painful-and-time-consuming-web-worker-development">Painful and Time-consuming Web Worker Development</h2><p>I cannot deny that by the javascript execution design (single-main-thread event-loop architecture), &#xA0;you can&apos;t have shared memories across threads(workers). </p><p>That&apos;s why web workers are designed in a message-driven way. All the communications are required to send message copies. For example:</p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">// main.js  
const first = document.querySelector(&apos;#number1&apos;);
const second = document.querySelector(&apos;#number2&apos;);

const result = document.querySelector(&apos;.result&apos;);

if (window.Worker) {
  const myWorker = new Worker(&quot;worker.js&quot;);

  first.onchange = function() {
    myWorker.postMessage([first.value, second.value]);
    console.log(&apos;Message posted to worker&apos;);
  }

  second.onchange = function() {
    myWorker.postMessage([first.value, second.value]);
    console.log(&apos;Message posted to worker&apos;);
  }

  myWorker.onmessage = function(e) {
    result.textContent = e.data;
    console.log(&apos;Message received from worker&apos;);
  }
} else {
  console.log(&apos;Your browser doesn\&apos;t support web workers.&apos;);
}</code></pre><figcaption>Example of MDN Web Docs</figcaption></figure><figure class="kg-card kg-code-card"><pre><code class="language-javascript">// worker.js
onmessage = function(e) {
  console.log(&apos;Worker: Message received from main script&apos;);
  const result = e.data[0] * e.data[1];
  if (isNaN(result)) {
    postMessage(&apos;Please write two numbers&apos;);
  } else {
    const workerResult = &apos;Result: &apos; + result;
    console.log(&apos;Worker: Posting message back to main script&apos;);
    postMessage(workerResult);
  }
}</code></pre><figcaption>Example of MDN Web Docs</figcaption></figure><p>It makes parallel processing difficult and requires a deep understanding of workers to do it right. </p><p>Here are the pain points that javascript application developers might encounter:</p><ul><li>creating a web worker and importing existing npm modules into a web worker is <em><strong>painful</strong></em>.</li><li>Coding in a weakly-typed language with a loosely message-driven style is <strong><em>not intuitive, time-consuming, more difficult to debug, and repetitive</em></strong>.</li><li>Working in an <em><strong>async loading UI pattern</strong></em> requires extra UI states to handle it.</li></ul><p>Because of the learning curve of web workers, we are tempted to do everything on UI thread like:</p><ul><li>calling fragmented JSON RPC / API calls and then <strong><em>aggregate into one view model</em></strong></li><li>calling a large GraphQL query and then applying a lot of <em><strong>data transformations</strong></em></li><li><em><strong>sorting, filtering, and reducing</strong></em> different kinds of data triggered by UI actions</li></ul><blockquote class="kg-blockquote-alt">In 2020, web technology advancement has brought the way out of this pain.</blockquote><h2 id="web-technology-advancement">Web Technology Advancement</h2><p>A <a href="https://github.com/GoogleChromeLabs/comlink?ref=intl.gaplo.tech">Comlink</a> RPC abstraction abstracts message-driven web workers into a promise-like RPC call. It works with webpack and magically import npm modules into the web workers.</p><p><a href="https://reactjs.org/docs/concurrent-mode-suspense.html?ref=intl.gaplo.tech">React 17.0 will expose </a><code><a href="https://reactjs.org/docs/concurrent-mode-suspense.html?ref=intl.gaplo.tech">&lt;Suspense&gt;</a></code><a href="https://reactjs.org/docs/concurrent-mode-suspense.html?ref=intl.gaplo.tech"> component </a>(Experimental <a href="https://reactjs.org/docs/concurrent-mode-intro.html?ref=intl.gaplo.tech">Concurrent Mode</a> Feature) helps boost developer productivity on handling asynchronous data <strong><em>without sacrificing the power of declarative programming style</em></strong>.</p><p>From my deep heart, I believe it is suitable for every React developer to discover those techniques, try to find out, and apply forward-suspense-compatible solutions into their new/existing code bases.</p><h2 id="creating-a-react-application-w-experimental-features">Creating a React Application w/ Experimental Features</h2><p>First, I studied the <a href="https://codesandbox.io/s/infallible-feather-xjtbu?file=%2Fsrc%2FfakeApi.js&amp;ref=intl.gaplo.tech">React 17.0 experimental </a><code><a href="https://codesandbox.io/s/infallible-feather-xjtbu?file=%2Fsrc%2FfakeApi.js&amp;ref=intl.gaplo.tech">&lt;Suspense&gt;</a></code><a href="https://codesandbox.io/s/infallible-feather-xjtbu?file=%2Fsrc%2FfakeApi.js&amp;ref=intl.gaplo.tech"> </a>contract and researched the latest Web Worker abstraction. </p><p>Combined with the power of <a href="https://github.com/GoogleChromeLabs/comlink?ref=intl.gaplo.tech">Comlink</a> RPC abstraction that abstracts message-driven web workers into a promise-like RPC call.</p><p>I created <a href="https://github.com/gaplo917/react-suspendable-contract?ref=intl.gaplo.tech">react-suspendable-contract</a> that is a high-level and experimental abstraction for any promise-like to fulfill the contract of <code>&lt;Suspense&gt;</code>. </p><pre><code class="language-typescript">import { DependencyList, ReactElement, useMemo } from &apos;react&apos;

const PENDING = Symbol(&apos;suspense.pending&apos;)
const SUCCESS = Symbol(&apos;suspense.success&apos;)
const ERROR = Symbol(&apos;suspense.error&apos;)

type RenderCallback&lt;T&gt; = (data: T) =&gt; ReactElement&lt;any, any&gt; | null

export interface SuspendableProps&lt;T&gt; {
  data: () =&gt; T
  children: RenderCallback&lt;T&gt;
}

/**
 * Simple Wrapper indicate the component is suspendable.
 * `useSuspendableData` will create the contract that consumed by &lt;Suspense&gt; Component
 *
 * ```js
 *  const UserPage = ({ userId }) =&gt; {
 *    const suspendableData = useSuspendableData(() =&gt; getUserAsync({ id: userId }), [userId])
 *
 *    return (
 *      &lt;Suspense fallback={&lt;Loading /&gt;}&gt;
 *         &lt;Suspendable data={suspendableData}&gt;
 *           {data =&gt; &lt;UserProfile user={data}/&gt;}
 *         &lt;/Suspendable&gt;
 *      &lt;/Suspense&gt;
 *    )
 *  }
 * ```
 *
 * @param data
 * @param children
 * @constructor
 */
export const Suspendable = &lt;T&gt;({ data, children }: SuspendableProps&lt;T&gt;) =&gt; {
  return children(data())
}

/**
 * `useSuspendableData` will only execute the promiseProvider when one of the `deps` has changed.
 *
 * Notes: internally use `useMemo` keep track the `deps` changes
 *
 * @param promiseProvider
 * @param deps
 */
export function useSuspendableData&lt;T&gt;(
  promiseProvider: () =&gt; PromiseLike&lt;T&gt;,
  deps: DependencyList | undefined,
): () =&gt; T {
  if (typeof promiseProvider !== &apos;function&apos;) {
    throw Error(&apos;promiseProvider is not a function&apos;)
  }

  return useMemo(() =&gt; {
    let status = PENDING
    let error: any
    let result: T
    const suspender = Promise.resolve()
      .then(() =&gt; promiseProvider())
      .then(r =&gt; {
        status = SUCCESS
        result = r
      })
      .catch(err =&gt; {
        status = ERROR
        error = err
      })
    return () =&gt; {
      switch (status) {
        case PENDING:
          throw suspender
        case ERROR:
          throw error
        case SUCCESS:
          return result
        default:
          throw Error(&apos;internal error. This should not happen&apos;)
      }
    }
  }, deps)
}</code></pre><p>Then, I created a sample React application with 300 items in a popular virtual list library <code>react-virtualized</code> . The application calculates a simple math function when the items become visible during the user&apos;s scroll. </p><p>I added a compute function that would burn some CPUs.</p><pre><code class="language-typescript">// Simply write the same computational function
// Comlink will do the magic to pack this function into a web worker

// noting special, just a compute task that could use CPU
export function compute(base: number, pow: number): number {
  let result = 0
  let i = 0
  const len = Math.pow(base, pow)
  while (i &lt; len) {
    result += Math.sin(i) * Math.sin(i) + Math.cos(i) * Math.cos(i)
    i++
  }

  return result
}</code></pre><p>I implemented four scenarios to process a virtual list calculation when the item becomes visible:</p><ul><li>UI Thread (Blocking)</li><li>Web Worker (Singleton)</li><li>Web Worker (Pool)</li><li>Web Worker (Dedicated Worker)</li></ul><p>Let&apos;s have a quick look at the final research deliverables.</p><h2 id="live-demo">Live Demo</h2><p>A video recording to show a web-worker-powered web application that can use all the 16 threads of an 8 core CPU.</p><!--kg-card-begin: markdown--><blockquote class="twitter-tweet"><p lang="en" dir="ltr">In 2020, If someone explains the laggy of a Web Application because of a single-threaded limitation, they don&apos;t know Web.<br><br>A correctly implemented Web Application can use ALL your CPU cores to process your business logic. <br><br>Live Demo: <a href="https://t.co/PpyfUbzKyJ?ref=intl.gaplo.tech">https://t.co/PpyfUbzKyJ</a> <a href="https://twitter.com/hashtag/React?src=hash&amp;ref_src=twsrc%5Etfw&amp;ref=intl.gaplo.tech">#React</a> <a href="https://twitter.com/hashtag/Javascript?src=hash&amp;ref_src=twsrc%5Etfw&amp;ref=intl.gaplo.tech">#Javascript</a> <a href="https://t.co/Ofh4rBbe1U?ref=intl.gaplo.tech">pic.twitter.com/Ofh4rBbe1U</a></p>&#x2014; LoGap (@gaplo917) <a href="https://twitter.com/gaplo917/status/1307647160192651266?ref_src=twsrc%5Etfw&amp;ref=intl.gaplo.tech">September 20, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script><!--kg-card-end: markdown--><p>Try the following iframe, or <a href="https://gaplo917.github.io/react-offload-ui-thread-research/?v=2&amp;ref=intl.gaplo.tech">Full Page Live Demo</a> to scroll in the &quot;Virtual List with 300 Items&quot;, and change the &quot;Mode&quot; to see the different effects!</p><!--kg-card-begin: markdown--><iframe style="background-color: white" height="600" src="https://gaplo917.github.io/react-offload-ui-thread-research/?v=2"></iframe><!--kg-card-end: markdown--><p></p><p>Someone might claim that:</p><blockquote class="kg-blockquote-alt">My application reaches 60 fps most of the time in my iPhone 12 Pro Max / i9 Macbook pro...</blockquote><p>Try to run the demo in the &quot;CPU 4x slowdown&quot; option under &quot;Performance.&quot;</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://intl.gaplo.tech/content/images/2022/02/offload-ui-thread-4x-slowdown.png" class="kg-image" alt="React Offload UI Thread Research" loading="lazy" width="2000" height="1476" srcset="https://intl.gaplo.tech/content/images/size/w600/2022/02/offload-ui-thread-4x-slowdown.png 600w, https://intl.gaplo.tech/content/images/size/w1000/2022/02/offload-ui-thread-4x-slowdown.png 1000w, https://intl.gaplo.tech/content/images/size/w1600/2022/02/offload-ui-thread-4x-slowdown.png 1600w, https://intl.gaplo.tech/content/images/size/w2400/2022/02/offload-ui-thread-4x-slowdown.png 2400w" sizes="(min-width: 720px) 720px"><figcaption>Intel i9 CPU 4x slowdown, UI Thread, dropped frames for 2 seconds.</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://intl.gaplo.tech/content/images/2022/02/offload-ui-thread-4x-slowdown-workers.png" class="kg-image" alt="React Offload UI Thread Research" loading="lazy" width="2000" height="1476" srcset="https://intl.gaplo.tech/content/images/size/w600/2022/02/offload-ui-thread-4x-slowdown-workers.png 600w, https://intl.gaplo.tech/content/images/size/w1000/2022/02/offload-ui-thread-4x-slowdown-workers.png 1000w, https://intl.gaplo.tech/content/images/size/w1600/2022/02/offload-ui-thread-4x-slowdown-workers.png 1600w, https://intl.gaplo.tech/content/images/size/w2400/2022/02/offload-ui-thread-4x-slowdown-workers.png 2400w" sizes="(min-width: 720px) 720px"><figcaption>Intel i9 CPU 4x slowdown, Web Workers(Singleton), no dropped frames</figcaption></figure><p>Hey! Do you know all the previous demos and screenshots only talk about 60 fps...</p><h2 id="120hz-is-coming">120Hz is coming</h2><p>Since the iPad Pro 2nd-gen started to support 120Hz, I believed 120Hz Web browsing is coming soon. The higher fps, the shorter time for a UI thread to process.</p><p>Considering the 60Hz to 120Hz change, the &quot;smooth UI (No dropped frames)&quot; process cycle time changed from 16.67ms to 8.33ms, which halved the time we got from the previous decade!</p><blockquote class="kg-blockquote-alt">An I/O call is non-blocking on a UI thread doesn&apos;t mean that it doesn&apos;t use the UI thread CPU time.</blockquote><p>A non-blocking HTTP I/O call doesn&apos;t block the UI thread immediately, but it does consume the UI thread&apos;s CPU time. If the UI thread doesn&apos;t have sufficient CPU time to process the UI, it would eventually drop the frames.</p><p>For instance, when the applications make high-concurrency I/O calls, these processes cumulatively eat up the UI thread&apos;s CPU time at the same moment and it would easily cause the drop frame. What&apos;s more, developers tend to process the business requirements after I/O calls such as data transformation, data validations, and UI state transformation, which are blocking operations.</p><p>If the business requirements become more and more complex, using web workers is unavoidable if you need to build a hassle-free, smooth 120Hz Web application.</p><p>A scientific approach is to develop your application in &quot;CPU 4x slowdown&quot; and target to remain 60 fps, it proves your application can smoothly complete the workload on a UI thread on low-end devices, and there should be no significant lag on high-end 120Hz browsing.</p><h2 id="understanding-the-trade-off">Understanding the Trade-off</h2><p>Assume we spend <code>n</code> CPU time to process a task on UI thread, if we offload the task to web workers would need to spend <code>n + m</code> CPU time where <code>m</code> is the overhead. Here are the illustrations:</p><figure class="kg-card kg-image-card"><img src="https://intl.gaplo.tech/content/images/2023/04/offload-webworker.png" class="kg-image" alt="React Offload UI Thread Research" loading="lazy" width="982" height="872" srcset="https://intl.gaplo.tech/content/images/size/w600/2023/04/offload-webworker.png 600w, https://intl.gaplo.tech/content/images/2023/04/offload-webworker.png 982w" sizes="(min-width: 720px) 720px"></figure><p>In a micro view, we traded off using more total CPU time to process the same task <em><strong>in parallel</strong></em> for saving the UI thread&apos;s CPU time. </p><p>But in a macro view, if the users did unnecessary and repetitive actions because of junking UI such as refreshing the whole page, it would easily take 100 - 1000x more CPU time than the overhead.</p><blockquote class="kg-blockquote-alt">If <code>m</code> is much smaller than <code>n</code>, it is worth reserving the UI thread CPU time for future complex changes.</blockquote><h2 id="conclusion">Conclusion </h2><p>The <a href="https://github.com/GoogleChromeLabs/comlink?ref=intl.gaplo.tech">ComLink abstraction</a>(turn a web worker to RPC-style function call) and <a href="https://reactjs.org/docs/concurrent-mode-intro.html?ref=intl.gaplo.tech" rel="nofollow">React Concurrent mode</a> arise.</p><p>I think it is time to start thinking about adopting web workers <em><strong>in all cases</strong></em> - <strong>completely decouple a data accessing layer. </strong>Using the browser&apos;s web worker in the background thread for all I/O and data processing by nature and then return to the UI thread for rendering.</p><p>Nothing is new; this is how we write a <strong><em>standard</em></strong> smooth frontend application in other platforms(iOS, Android, Windows, macOS, JVM) since multi-threaded CPU arise.</p><p>GitHub Source: <a href="https://github.com/gaplo917/react-offload-ui-thread-research?ref=intl.gaplo.tech">https://github.com/gaplo917/react-offload-ui-thread-research</a></p><h2 id="appendix">Appendix </h2><h4 id="ui-thread-blocking">UI Thread (Blocking)</h4><p>The one who is already busy on UI still needs to work on the calculation task.</p><figure class="kg-card kg-code-card"><pre><code class="language-tsx">// file:SomeListBlocking.tsx

function TabContent({ index, base, pow, style, isScrolling }: TabContentProps) {
  // if user is scrolling and the content is not yet ready, showing loading component
  const result = isScrolling ? (
    &lt;Loading index={index} /&gt;
  ) : (
    useMemo(() =&gt; {
      const result = compute(base, pow)
      return (
        &lt;ComputeResult index={index} base={base} pow={pow} result={result} /&gt;
      )
    }, [base, pow])
  )
  return &lt;p style={{ padding: 8, ...style }}&gt;{result}&lt;/p&gt;
}

export default function SomeListBlocking() {
  return (
    &lt;VirtualList
      rowRendererProvider={(base, pow) =&gt; ({
        key,
        index,
        style,
        isScrolling,
      }) =&gt; (
        &lt;TabContent
          key={key}
          index={index}
          base={base}
          pow={pow}
          style={style}
          isScrolling={isScrolling}
        /&gt;
      )}
    /&gt;
  )
}</code></pre><figcaption>laggy</figcaption></figure><h4 id="web-worker-singleton">Web Worker (Singleton)</h4><p>An extra worker to do all the computations. It is the easiest way to integrate Comlink correctly and 99.99% guarantees performance improvement.</p><pre><code class="language-tsx">// file:SomeListSingleton.tsx
// the actual web worker function from comlink
import { compute } from &apos;../../workers/compute.worker.singleton&apos;

function TabContent({ index, base, pow, style }: TabContentProps) {
  // create a future data that force a promise to fulfill  Suspense Component contract
  const suspendableData = useSuspendableData(() =&gt; compute(base, pow), [
    base,
    pow,
  ])
  return (
    &lt;p style={{ padding: 8, ...style }}&gt;
      &lt;ErrorBoundary
        key={`${base}-${pow}`}
        fallback={&lt;ComputeErrorMessage index={index} base={base} pow={pow} /&gt;}
      &gt;
        &lt;React.Suspense fallback={&lt;Loading index={index} /&gt;}&gt;
          &lt;Suspendable data={suspendableData}&gt;
            {(data) =&gt; (
              &lt;ComputeResult
                index={index}
                base={base}
                pow={pow}
                result={data}
              /&gt;
            )}
          &lt;/Suspendable&gt;
        &lt;/React.Suspense&gt;
      &lt;/ErrorBoundary&gt;
    &lt;/p&gt;
  )
}

export default function SomeListSingleton() {
  return (
    &lt;VirtualList
      rowRendererProvider={(base, pow) =&gt; ({ key, index, style }) =&gt; (
        &lt;TabContent
          key={key}
          index={index}
          base={base}
          pow={pow}
          style={style}
        /&gt;
      )}
    /&gt;
  )
}</code></pre><h4 id="web-worker-pool">Web Worker (Pool)</h4><p>When you have a lot of computations, you might need a worker pool and distribute the jobs to the workers in parallel to speed up the calculations.</p><pre><code class="language-tsx">// file:SomeListWorkerPool.tsx
// the actual web worker from comlink
import ComputeWorker from &apos;comlink-loader!../../workers/compute.worker&apos;

function TabContent({ index, base, pow, style, worker }: TabContentProps) {
  const suspendableData = useSuspendableData&lt;number&gt;(
    () =&gt; worker.compute(base, pow),
    [base, pow],
  )
  return (
    &lt;p style={{ padding: 8, ...style }}&gt;
      &lt;ErrorBoundary
        key={`${base}-${pow}`}
        fallback={&lt;ComputeErrorMessage index={index} base={base} pow={pow} /&gt;}
      &gt;
        &lt;React.Suspense fallback={&lt;Loading index={index} /&gt;}&gt;
          &lt;Suspendable data={suspendableData}&gt;
            {(data) =&gt; (
              &lt;ComputeResult
                index={index}
                base={base}
                pow={pow}
                result={data}
              /&gt;
            )}
          &lt;/Suspendable&gt;
        &lt;/React.Suspense&gt;
      &lt;/ErrorBoundary&gt;
    &lt;/p&gt;
  )
}

export default function SomeListWorkerPool() {
  const [poolSize, setPoolSize] = useState(4)

  // create a pool of workers
  const workerPool = useMemo(
    () =&gt; new Array(poolSize).fill(null).map(() =&gt; new ComputeWorker()),
    [poolSize],
  )

  return (
    &lt;&gt;
      &lt;VirtualList
        headerComp={() =&gt; (
          &lt;TextField
            id=&quot;standard-basic&quot;
            label=&quot;Worker Pool Size&quot;
            required
            type=&quot;number&quot;
            variant=&quot;outlined&quot;
            defaultValue={poolSize}
            inputProps={{ step: 1 }}
            onChange={(event: React.ChangeEvent&lt;{ value: string }&gt;) =&gt; {
              setPoolSize(Number(event.target.value))
            }}
          /&gt;
        )}
        rowRendererProvider={(base, pow) =&gt; ({ key, index, style }) =&gt; (
          &lt;TabContent
            key={key}
            index={index}
            base={base}
            pow={pow}
            style={style}
            worker={workerPool[index % poolSize] /* distribute workload */}
          /&gt;
        )}
      /&gt;
    &lt;/&gt;
  )
}</code></pre><h4 id="web-worker-dedicated-worker">Web Worker (Dedicated Worker)</h4><p>Ideal world, when you have unlimited resources, you could dedicate a new task to a new worker. In the real world, it is not efficient.</p><pre><code class="language-tsx">// file:SomeListDedicatedWorker.tsx
// the actual web worker from comlink
import ComputeWorker from &apos;comlink-loader!../../workers/compute.worker&apos;


// no way to dispose the worker if using a comlink-loader, until this pr is merged
// https://github.com/GoogleChromeLabs/comlink-loader/pull/27
function TabContent({ index, base, pow, style }: TabContentProps) {
  const suspendableData = useSuspendableData&lt;number&gt;(async () =&gt; {
    // create a dedicated worker for each rendering
    const worker = new ComputeWorker()
    return worker.compute(base, pow)
  }, [base, pow])

  return (
    &lt;p style={{ padding: 8, ...style }}&gt;
      &lt;ErrorBoundary
        key={`${base}-${pow}`}
        fallback={&lt;ComputeErrorMessage index={index} base={base} pow={pow} /&gt;}
      &gt;
        &lt;React.Suspense fallback={&lt;Loading index={index} /&gt;}&gt;
          &lt;Suspendable data={suspendableData}&gt;
            {(data) =&gt; (
              &lt;ComputeResult
                index={index}
                base={base}
                pow={pow}
                result={data}
              /&gt;
            )}
          &lt;/Suspendable&gt;
        &lt;/React.Suspense&gt;
      &lt;/ErrorBoundary&gt;
    &lt;/p&gt;
  )
}

export default function SomeListDedicatedWorker() {
  return (
    &lt;VirtualList
      rowRendererProvider={(base, pow) =&gt; ({ key, index, style }) =&gt; (
        &lt;TabContent
          key={key}
          index={index}
          base={base}
          pow={pow}
          style={style}
        /&gt;
      )}
    /&gt;
  )
}</code></pre><h2 id="patreon-backers">Patreon Backers</h2><p>Thank you for being so supportive &#x2764;&#xFE0F;&#x2764;&#xFE0F;&#x2764;&#xFE0F;. Your support made this R&amp;D article always free, open, and accessible. It also allows me to focus on my writings that bring a positive impact to you and the other readers among the global technical community. <a href="https://www.patreon.com/gaplotech?ref=intl.gaplo.tech">Become a Patreon Backer!</a></p><p><strong>Backers:</strong></p><!--kg-card-begin: markdown--><ul class="backer-container">
    <li>
        <div class="logo-container">
            <img src="https://storage.googleapis.com/assets.gaplo.tech/sp/tecky_logo.png" alt="React Offload UI Thread Research"></div>
        <div>
            <a href="https://tecky.io/?ref=intl.gaplo.tech" target="_blank">Tecky Academy(Business I)</a>
        </div>
    </li>
    <li>Anthony Siu (Gold)</li>
    <li>Martin Ho (Gold)</li>
    <li>Chan Paul (Gold)</li>
    <li>Jay Chau (Gold)</li>
    <li>Mat (Gold)</li>
    <li>Raymond An</li>
    <li>Chi Hang Chan</li>
</ul><!--kg-card-end: markdown--><p><a href="https://www.patreon.com/gaplotech?ref=intl.gaplo.tech">Become a Patreon Backer!</a></p>]]></content:encoded></item><item><title><![CDATA[The Declarative Cost in Javascript]]></title><description><![CDATA[benchmarking in 19 different implementations for demonstrating declarative cost]]></description><link>https://intl.gaplo.tech/the-declarative-cost-in-javascript/</link><guid isPermaLink="false">61c447181fe2271b4292fcae</guid><category><![CDATA[Research]]></category><category><![CDATA[javascript]]></category><category><![CDATA[benchmark]]></category><dc:creator><![CDATA[Gap]]></dc:creator><pubDate>Fri, 04 Sep 2020 10:00:00 GMT</pubDate><media:content url="https://intl.gaplo.tech/content/images/2021/12/js-declarative-cost.png" medium="image"/><content:encoded><![CDATA[<div class="kg-card kg-callout-card kg-callout-card-blue"><div class="kg-callout-emoji">&#x1F37E;</div><div class="kg-callout-text">This article was published on <a href="https://www.patreon.com/gaplotech?ref=intl.gaplo.tech">Patreon members-only</a> in early Sep 2020. In 2022, I decided to bring it back to my blog and set it free and open for everyone in the world.<br><br>If you find this article bringing you new insight, <a href="https://www.patreon.com/gaplotech?ref=intl.gaplo.tech">please consider joining Patreon to show your support!</a></div></div><h2 id="foreword">Foreword</h2><img src="https://intl.gaplo.tech/content/images/2021/12/js-declarative-cost.png" alt="The Declarative Cost in Javascript"><p>In Hong Kong, there are so many code boot camps teaching javascript courses that market with the buzzword - Full Stack developer. In the code boot camp, the candidates are trained to figure out the frontend (React), backend (Nodejs), and database(NoSQL) in a short period. As we all know, each of these fields is a super big spectrum that takes time to discover and study. I am pretty confident to say that:</p><blockquote class="kg-blockquote-alt">It is easy to get it working but challenging to do it right.</blockquote><p>The root of doing the right thing is entirely relying on the fundamentals - knowledge of Software Engineering. It is a big topic that I would continue to write articles to share more.</p><p>Let&apos;s back to the topic - The javascript declarative cost.</p><h2 id="declarative-javascript-functional-programming">Declarative Javascript (Functional Programming)</h2><p>ES6 has introduced functional operators(<code><em>.map</em></code><em>, </em><code><em>.filter</em></code><em>, </em><code><em>.reduce</em></code>) for Array. I can&apos;t remember the last time I wrote &quot;for loop&quot; control flow to compute data in javascript.</p><blockquote>I really like the power of declarative programming style that helps me write nearly bug-free and scalable data transformations</blockquote><p>I decided to start building a new project called <a href="https://github.com/gaplo917/kt.ts?ref=intl.gaplo.tech" rel="nofollow noopener">kt.ts</a> that targeted to bring a <a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/?ref=intl.gaplo.tech">Kotlin wise-designed collections API interface</a> to Typescript that the existing npm modules can&apos;t compete with.</p><p>In Javascript, there are two popular FP libraries called <code>lodash</code> and <code>ramda</code>, developers write data transformations in a self-documented and declarative way.</p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">// lodash lazy chain
_.chain(arr)
    .map(someTransform)
    .filter(isNotNullPredicate)
    .uniq()
    .sumBy(someCalculation)
    .value()

// ramda
R.pipe(
  R.map(someTransform),
  R.filter(isNotNullPredicate),
  R.uniq,
  R.map(someCalculation),
  R.sum,
)(array)</code></pre><figcaption>Chain with many different single-purpose operators</figcaption></figure><p>To do the same express in Kotlin, we could express in a simpler way:</p><figure class="kg-card kg-code-card"><pre><code class="language-kotlin">array
  .mapNotNull(someTransform)
  .distinct()
  .sumBy(someCalculation)</code></pre><figcaption>I want to build the same Kotlin interfaces in Typescript on kt.ts npm modules.</figcaption></figure><p>That&apos;s why I would like to bring these wise and practical designed interfaces from Kotlin to Typescript.</p><h2 id="creating-ktts-in-a-wrapper-approach">Creating kt.ts in a wrapper approach</h2><p>So, I started with a wrapper approach that uses <code>lodash</code> internally to build the kt.ts abstractions. After a few hours of implementation and designing the project architecture, a few commonly used interfaces are finished like <em><code>.mapNotNull(transform)</code> , <code>.distinct()</code>.</em></p><p>Here is an example of the wrapper approach in Typescript:</p><figure class="kg-card kg-code-card"><pre><code class="language-typescript">import { uniq, uniqBy } from &apos;lodash&apos;

declare global {
  interface Array&lt;T&gt; extends KtListDistinctOp&lt;T&gt; {}
}

export interface KtListDistinctOp&lt;T&gt; {
  /**
   * Returns a list containing only distinct elements from the given collection.
   *
   * The elements in the resulting list are in the same order as they were in the source collection.
   */
  distinct(): T[]

  /**
   * Returns a list containing only elements from the given collection
   * having distinct keys returned by the given [selector] function.
   *
   * The elements in the resulting list are in the same order as they were in the source collection.
   */
  distinctBy&lt;K&gt;(selector: (value: T) =&gt; K): T[]
}

export default &lt;T extends any&gt;(proto: Array&lt;T&gt;) =&gt; {
  proto.distinct = function (): T[] {
    return uniq(this)
  }

  proto.distinctBy = function &lt;R&gt;(selector: (value: T) =&gt; R) {
    return uniqBy(this, selector)
  }
}</code></pre><figcaption>expose the kotlin-like-interface on array prototype and internally use lodash implementation</figcaption></figure><p>After that, I kick-started another project to benchmark my wrapped implementations in kt.ts to guarantee the implementation doesn&apos;t have performance regression. I wrote the vanilla for-loop approach to compare my kt.ts implementation. </p><p>The benchmark result is very disappointed and completely unacceptable. The first thought that came to my head was</p><blockquote>Did I do something wrong? Is that V8 optimization too magical to me? Or lodash issue?</blockquote><blockquote class="kg-blockquote-alt">OMFG, 80x performance difference!?</blockquote><p>To take care of my curiosity,</p><blockquote><em>my sense of software engineering summon me to implement all possible versions of &#xA0;<code>.mapNotNull</code> , <code>.distinct</code> , and <code>.sumBy</code> in vanilla javascript and different libraries.</em></blockquote><div class="kg-card kg-callout-card kg-callout-card-green"><div class="kg-callout-emoji">&#x1F4A1;</div><div class="kg-callout-text">With the upcoming findings, I created a simple javascript exercise <u><a href="https://github.com/gaplotech/SB03-js-declarative-cost?ref=intl.gaplo.tech" rel="nofollow noopener">https://github.com/gaplotech/SB03-js-declarative-cost</a></u> to ignite other software engineers to learn more about the &quot;declarative cost in javascript&quot; by themselves.</div></div><h2 id="preparation-of-javascript-benchmark">Preparation of Javascript Benchmark</h2><p>To kick start, I pick a common scenario that uses <code><em>.mapNotNull</em></code><em> , </em><code><em>.distinct</em></code><em> , and </em><code><em>.sumBy</em></code> that use <code>native</code>, <code>ramda</code>, <code>lodash</code>, <code>array prototype</code>, and <code>lazy iterator</code> to implement the same data transformation logic.</p><p>Finally, I implemented 19 variations that are (click to browse the source code)</p><ul><li><a href="https://github.com/gaplo917/js-benchmark/blob/master/src/node/perf-impl/ideal.mjs?ref=intl.gaplo.tech#L23-L44">native-ideal-while</a></li><li><a href="https://github.com/gaplo917/js-benchmark/blob/master/src/node/perf-impl/ideal.mjs?ref=intl.gaplo.tech#L3-L21">native-standard-for-loop</a></li><li><a href="https://github.com/gaplo917/js-benchmark/blob/master/src/node/perf-impl/nativeFunctional.mjs?ref=intl.gaplo.tech"><a href="https://github.com/gaplo917/js-benchmark/blob/master/src/node/perf-impl/nativeFunctional.mjs?ref=intl.gaplo.tech#L37-L41">native-fp (declarative, native <em><code>.map</code>, <code>.filter</code>, <code>.reduce</code></em>)</a></a></li><li><a href="https://github.com/gaplo917/js-benchmark/blob/master/src/node/perf-impl/nativeFunctional.mjs?ref=intl.gaplo.tech#L43-L49">native-fp-optimized (merged FP operators)</a></li><li><a href="https://github.com/gaplo917/js-benchmark/blob/master/src/node/perf-impl/nativeFunctional.mjs?ref=intl.gaplo.tech#L23-L33">native-fp-reduce-imperative(infamous wrong way of using reduce)</a></li><li><a href="https://github.com/gaplo917/js-benchmark/blob/master/src/node/perf-impl/lodashImpl.mjs?ref=intl.gaplo.tech#L57-L62">lodash-one-by-one</a></li><li><a href="https://github.com/gaplo917/js-benchmark/blob/master/src/node/perf-impl/lodashImpl.mjs?ref=intl.gaplo.tech#L64-L69">lodash-one-by-one-optimized</a></li><li><a href="https://github.com/gaplo917/js-benchmark/blob/master/src/node/perf-impl/lodashImpl.mjs?ref=intl.gaplo.tech#L71-L78">lodash-lazy-chain</a></li><li><a href="https://github.com/gaplo917/js-benchmark/blob/master/src/node/perf-impl/lodashImpl.mjs?ref=intl.gaplo.tech#L80-L86">lodash-lazy-chain-optimized</a></li><li><a href="https://github.com/gaplo917/js-benchmark/blob/master/src/node/perf-impl/lodashImpl.mjs?ref=intl.gaplo.tech#L88-L103"><a href="https://github.com/gaplo917/js-benchmark/blob/master/src/node/perf-impl/lodashImpl.mjs?ref=intl.gaplo.tech#L88-L95">lodash-fp</a></a></li><li><a href="https://github.com/gaplo917/js-benchmark/blob/master/src/node/perf-impl/lodashImpl.mjs?ref=intl.gaplo.tech#L97-L103">l<a href="https://github.com/gaplo917/js-benchmark/blob/master/src/node/perf-impl/lodashImpl.mjs?ref=intl.gaplo.tech#L97-L103">odash-fp-optimized</a></a></li><li><a href="https://github.com/gaplo917/js-benchmark/blob/master/src/node/perf-impl/ramdaImpl.mjs?ref=intl.gaplo.tech"><a href="https://github.com/gaplo917/js-benchmark/blob/master/src/node/perf-impl/ramdaImpl.mjs?ref=intl.gaplo.tech#L60-L68">ramda</a></a></li><li><a href="https://github.com/gaplo917/js-benchmark/blob/master/src/node/perf-impl/ramdaImpl.mjs?ref=intl.gaplo.tech#L70-L76">ramda-optimized</a></li><li><a href="https://github.com/gaplo917/js-benchmark/blob/master/src/node/perf-impl/native.mjs?ref=intl.gaplo.tech#L125-L130">array-ext-native</a> (kt.ts designed)</li><li><a href="https://github.com/gaplo917/js-benchmark/blob/master/src/node/perf-impl/lodashImpl.mjs?ref=intl.gaplo.tech#L105-L111">array-ext-lodash-optmized</a> (kt.ts designed)</li><li><a href="https://github.com/gaplo917/js-benchmark/blob/master/src/node/perf-impl/ramdaImpl.mjs?ref=intl.gaplo.tech#L78-L83">array-ext-ramda-optmized</a> (kt.ts designed)</li><li><a href="https://github.com/gaplo917/js-benchmark/blob/master/src/node/perf-impl/native.mjs?ref=intl.gaplo.tech#L132-L138">lazy-sequence-native</a></li><li><a href="https://github.com/gaplo917/js-benchmark/blob/master/src/node/perf-impl/lodashImpl.mjs?ref=intl.gaplo.tech#L113-L119">lazy-sequence-lodash-optmized</a></li><li><a href="https://github.com/gaplo917/js-benchmark/blob/master/src/node/perf-impl/ramdaImpl.mjs?ref=intl.gaplo.tech#L85-L91">lazy-sequence-ramda-optmized</a></li></ul><p>Then, I use <a href="https://www.npmjs.com/package/benchmark?ref=intl.gaplo.tech">benchmark npm library</a> to run with <code>arraySize = [1,10,100,1k,10k,100k,1M]</code>` in node.js v14.</p><blockquote class="kg-blockquote-alt">Those implementations are so sensitive to CPU performance. </blockquote><p>I have to guarantee there is </p><ul><li>No unwanted applications are running</li><li>No noisy neighbor on cloud</li><li>No performance thermal throttling</li><li>Large enough sample size to take the average</li></ul><p>Thus, I rented a cloud-based Packet&apos;s Bare Metal Ubuntu Server <a href="https://www.packet.com/cloud/servers/t1-small/?ref=intl.gaplo.tech" rel="nofollow noopener">(t1.small.x86)</a>. Run <code><em>1000 sample * 20 implementations * 7 size</em></code><em>.</em> Here is the benchmark performance:</p><!--kg-card-begin: markdown--><table>
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>Machine</td>
<td>t1.small.x86 (bare metal)</td>
</tr>
<tr>
<td>CPU</td>
<td>4 Physical Cores @ 2.4 GHz (1 &#xD7; ATOM C2550)</td>
</tr>
<tr>
<td>Node</td>
<td>v14.9.0</td>
</tr>
<tr>
<td>Linux</td>
<td>Ubuntu 20.04</td>
</tr>
<tr>
<td>Min Sample</td>
<td>1000</td>
</tr>
<tr>
<td>Duration</td>
<td>16.44 hours</td>
</tr>
<tr>
<td>Script</td>
<td>node --max-old-space-size=4096 src/node/perf.mjs</td>
</tr>
<tr>
<td>Repository</td>
<td><a href="https://github.com/gaplo917/js-benchmark?ref=intl.gaplo.tech">https://github.com/gaplo917/js-benchmark</a></td>
</tr>
</tbody>
</table>
<!--kg-card-end: markdown--><h2 id="stunning-benchmark-result">Stunning Benchmark Result</h2><p>As a result, the whole benchmark <code><em>1000 sample * 20 implementations * 7 size</em></code> it takes 16.44 hours to run! </p><p>Here is the result:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://intl.gaplo.tech/content/images/2022/02/JS-Benchmark--Array-Size---1-.png" class="kg-image" alt="The Declarative Cost in Javascript" loading="lazy" width="934" height="584" srcset="https://intl.gaplo.tech/content/images/size/w600/2022/02/JS-Benchmark--Array-Size---1-.png 600w, https://intl.gaplo.tech/content/images/2022/02/JS-Benchmark--Array-Size---1-.png 934w" sizes="(min-width: 720px) 720px"><figcaption>native-ideal-while is the best, ramda is 80x slower than the best</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://intl.gaplo.tech/content/images/2022/02/JS-Benchmark--Array-Size---10-.png" class="kg-image" alt="The Declarative Cost in Javascript" loading="lazy" width="924" height="584" srcset="https://intl.gaplo.tech/content/images/size/w600/2022/02/JS-Benchmark--Array-Size---10-.png 600w, https://intl.gaplo.tech/content/images/2022/02/JS-Benchmark--Array-Size---10-.png 924w" sizes="(min-width: 720px) 720px"><figcaption>native-fp-* are getting closer to native-*</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://intl.gaplo.tech/content/images/2022/02/JS-Benchmark--Array-Size---100-.png" class="kg-image" alt="The Declarative Cost in Javascript" loading="lazy" width="934" height="584" srcset="https://intl.gaplo.tech/content/images/size/w600/2022/02/JS-Benchmark--Array-Size---100-.png 600w, https://intl.gaplo.tech/content/images/2022/02/JS-Benchmark--Array-Size---100-.png 934w" sizes="(min-width: 720px) 720px"><figcaption>lodash-* / ramda-* without merge operator optimizations are 20x slower than the best</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://intl.gaplo.tech/content/images/2022/02/JS-Benchmark--Array-Size---1-000-.png" class="kg-image" alt="The Declarative Cost in Javascript" loading="lazy" width="924" height="584" srcset="https://intl.gaplo.tech/content/images/size/w600/2022/02/JS-Benchmark--Array-Size---1-000-.png 600w, https://intl.gaplo.tech/content/images/2022/02/JS-Benchmark--Array-Size---1-000-.png 924w" sizes="(min-width: 720px) 720px"><figcaption>ramda is 3.5x slower than lodash</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://intl.gaplo.tech/content/images/2022/02/JS-Benchmark--Array-Size---10-000-.png" class="kg-image" alt="The Declarative Cost in Javascript" loading="lazy" width="934" height="584" srcset="https://intl.gaplo.tech/content/images/size/w600/2022/02/JS-Benchmark--Array-Size---10-000-.png 600w, https://intl.gaplo.tech/content/images/2022/02/JS-Benchmark--Array-Size---10-000-.png 934w" sizes="(min-width: 720px) 720px"><figcaption>shows lodash-fp has an internal optimization when the array size is big</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://intl.gaplo.tech/content/images/2022/02/JS-Benchmark--Array-Size---100-000-.png" class="kg-image" alt="The Declarative Cost in Javascript" loading="lazy" width="924" height="584" srcset="https://intl.gaplo.tech/content/images/size/w600/2022/02/JS-Benchmark--Array-Size---100-000-.png 600w, https://intl.gaplo.tech/content/images/2022/02/JS-Benchmark--Array-Size---100-000-.png 924w" sizes="(min-width: 720px) 720px"><figcaption>difference getting smaller for all implementations</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://intl.gaplo.tech/content/images/2022/02/JS-Benchmark--Array-Size---1-000-000-.png" class="kg-image" alt="The Declarative Cost in Javascript" loading="lazy" width="934" height="584" srcset="https://intl.gaplo.tech/content/images/size/w600/2022/02/JS-Benchmark--Array-Size---1-000-000-.png 600w, https://intl.gaplo.tech/content/images/2022/02/JS-Benchmark--Array-Size---1-000-000-.png 934w" sizes="(min-width: 720px) 720px"><figcaption>native-fp is ~2x slower than the native-fp-optimized, array-ext-native that splitting multiple for-loops is faster</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://intl.gaplo.tech/content/images/2022/02/Js-Benchmark--Log-Scale-.png" class="kg-image" alt="The Declarative Cost in Javascript" loading="lazy" width="1368" height="846" srcset="https://intl.gaplo.tech/content/images/size/w600/2022/02/Js-Benchmark--Log-Scale-.png 600w, https://intl.gaplo.tech/content/images/size/w1000/2022/02/Js-Benchmark--Log-Scale-.png 1000w, https://intl.gaplo.tech/content/images/2022/02/Js-Benchmark--Log-Scale-.png 1368w" sizes="(min-width: 720px) 720px"><figcaption>Log scale</figcaption></figure><p>Summary:</p><ul><li>The imperative approach performs the best when the array size is small</li><li>Merged functional operators significantly improve the performance, compare <code>native-fp</code> and <code>native-fp-optimized</code>, <a href="https://github.com/gaplo917/js-benchmark/blob/master/src/node/perf-impl/nativeFunctional.mjs?ref=intl.gaplo.tech#L37-L49">see the optimized codes</a></li><li>The bigger the array size, the closer between <code>native-fp-optimized</code> and <code>native-ideal-while</code>. The performance of the <code>native-fp-optimized</code> is very close to the <code>native-ideal-while</code> when the <code>array size &gt;= 100</code>. </li><li>The <code>ramda</code> and <code>lodash/fp</code>&apos;s benchmark results are surprisingly worse when the <code>array size &lt; 100</code></li><li>Surprisingly, comparing between <code>native-standard-for-loop</code> and <code>array-ext-native</code>, <code>array-ext-native</code> is a declarative abstraction that uses two <code>for</code> loop implementations and one <code>new Set</code> &#xA0;underlying to avoid <code>Set</code> the operation does improve the performance when the array size is large. </li></ul><pre><code class="language-javascript">// native-standard-for-loop
export function nativeStandard(arr) {
  const len = arr.length
  if (len === 0) {
    return 0
  }
  const set = new Set()
  let sum = 0
  for (let i = 0; i &lt; len; i++) {
    const e = someTransform(arr[i])
    if (e !== null) {
      const oriSize = set.size
      set.add(e)
      if (set.size !== oriSize) {
        sum += someCalculation(e)
      }
    }
  }
  return sum
}

// array-ext-native
export function arrayExtensionNative(arr) {
  return arr
    .ktMapNotNullNative(someTransform)
    .ktDistinctNative()
    .ktSumByNative(someCalculation)
}

Array.prototype.ktMapNotNullNative = function (transform) {
  const result = []
  for (let i = 0, len = this.length; i &lt; len; i++) {
    const e = transform(this[i])
    if (e !== null) {
      result.push(e)
    }
  }
  return result
}

Array.prototype.ktDistinctNative = function () {
  return Array.from(new Set(this))
}

Array.prototype.ktSumByNative = function (selector) {
  let sum = 0
  for (let i = 0, len = this.length; i &lt; len; i++) {
    sum += selector(this[i])
  }
  return sum
}</code></pre><p>The benchmark proves that if kt.ts can bring <a href="https://github.com/gaplo917/kt.ts?ref=intl.gaplo.tech#project-status">Kotlin collection API design</a>(lots of extra declarative operators) to Typescript, Typescript developers can </p><ul><li>enjoy shorter implementation and more performant declarative codes compared with the <code>native-fp</code> / <code>lodash</code> / <code>ramda</code></li><li>enjoy a lot of extra declarative operators (merged multiple functional operators) to build different use cases</li></ul><p>I believe 80%+ of worldwide javascript usage won&apos;t process an array having more than 1k items, and 80%+ developers won&apos;t choose javascript to build performance-sensitive applications.</p><blockquote class="kg-blockquote-alt">A professional software engineer should always know their needs. </blockquote><blockquote class="kg-blockquote-alt">Don&apos;t worry about non-exist needs.</blockquote><p>If <strong>the performance is critical </strong>in most use cases, I suggest learning the necessary algorithms and data structures and writing them in an imperative style(control flow) to get the best performance rather than spending time struggling on the performance tuning of the declarative programming style.</p><p>If <strong>the performance is neglectable </strong>in most use cases, I suggest going for declarative programming style to use well-tested functional operators to write a more readable and self-documented data transformation logic.</p><h2 id="conclusion">Conclusion</h2><p>By kicking start a new side project, kt.ts, I learned a lot from creating this benchmark of figuring out the javascript declarative cost. This benchmark has changed my mind about using <code>lodash</code> or <code>ramda</code> as a foundation library.</p><p>Learning <code>lodash</code> or <code>ramda</code> is just a way to work in a more functional and declarative. They did provide well-tested functional operators and allowed developers to use them immediately. However, from the benchmark result, the implementation itself doesn&apos;t work efficiently in javascript compared to the native functional operators.</p><p>Unless their internal implementations would eventually provide a &quot;Parallel Processing&quot; option that <em><strong><strong>spawns an extra node </strong>worker/web worker<strong> </strong></strong>and<strong><strong> process array elements in parallel</strong>, </strong></em><a href="https://docs.scala-lang.org/overviews/parallel-collections/overview.html?ref=intl.gaplo.tech">like Scala&apos;s parallel collection</a>, otherwise, under a single main thread execution model, you need to sacrifice some performance to trade readability and development speed. </p><blockquote class="kg-blockquote-alt">That is the &quot;Declarative Cost in Javascript&quot;.</blockquote><p>If someone told you a simple <code><em>.map</em></code><em> or </em><code><em>.filter</em></code> operator in <code>lodash</code> or <code>ramda</code> <strong>is just n% slower than</strong> the native <code><em>.map</em></code> or <code>.filter</code> operator, they forget that we normally chain 3 - 5 operators on average that is <strong>(1-n)^k % slower.</strong></p><p>If on average <code>n = 0.2</code>, chain 3 - 5 operators become</p><ul><li><em>0.8^3 = 0.51</em></li><li><em>0.8^5 = 0.32</em></li></ul><blockquote class="kg-blockquote-alt">~50-70% performance drop on average <br>if <br>chaining 3 - 5 operators</blockquote><p>I hope you can get some insight from this article!</p><h2 id="patreon-backers">Patreon Backers</h2><p>Thank you for being so supportive &#x2764;&#xFE0F;&#x2764;&#xFE0F;&#x2764;&#xFE0F;. Your support made this R&amp;D article always free, open, and accessible. It also allows me to focus on my writings that bring a positive impact to you and the other readers among the global technical community. <a href="https://www.patreon.com/gaplotech?ref=intl.gaplo.tech">Become a Patreon Backer!</a></p><p><strong>Backers:</strong></p><!--kg-card-begin: markdown--><ul class="backer-container">
    <li>
        <div class="logo-container">
            <img src="https://storage.googleapis.com/assets.gaplo.tech/sp/tecky_logo.png" alt="The Declarative Cost in Javascript"></div>
        <div>
            <a href="https://tecky.io/?ref=intl.gaplo.tech" target="_blank">Tecky Academy(Business I)</a>
        </div>
    </li>
    <li>Anthony Siu (Gold)</li>
    <li>Martin Ho (Gold)</li>
    <li>Chan Paul (Gold)</li>
    <li>Jay Chau (Gold)</li>
    <li>Mat (Gold)</li>
    <li>Raymond An</li>
    <li>Chi Hang Chan</li>
</ul><!--kg-card-end: markdown--><p><a href="https://www.patreon.com/gaplotech?ref=intl.gaplo.tech">Become a Patreon Backer!</a></p>]]></content:encoded></item><item><title><![CDATA[Part 3 : Picking Kotlin for Android — Swift in Android?]]></title><description><![CDATA[An iOS developer perspective]]></description><link>https://intl.gaplo.tech/picking-kotlin-for-part-3/</link><guid isPermaLink="false">5e6e055423787c76f0676d76</guid><category><![CDATA[kotlin]]></category><category><![CDATA[android]]></category><category><![CDATA[app]]></category><dc:creator><![CDATA[Gap]]></dc:creator><pubDate>Sat, 08 Apr 2017 14:00:00 GMT</pubDate><media:content url="https://intl.gaplo.tech/content/images/2020/03/1_tcsC49oEgeDK8pacA1YLbw-2.png" medium="image"/><content:encoded><![CDATA[<h1 id="kotlin-is-maturer-unlike-swift-1-x-2-x">Kotlin is maturer, unlike Swift 1.x/2.x</h1><img src="https://intl.gaplo.tech/content/images/2020/03/1_tcsC49oEgeDK8pacA1YLbw-2.png" alt="Part 3 : Picking Kotlin for Android &#x2014; Swift in Android?"><p>I have seen a lot of iOS developers who has tried Kotlin with their first glaze and most of them would very likely to use the following statement to describe Kotlin:</p><blockquote>&#x201C;Kotlin in Android&#x201D; is just like &#x201C;Swift in iOS&#x201D;</blockquote><p>But, from my personal experience on Android &amp; iOS development. I think &#x201C;Kotlin in Android&#x201D; is <strong><strong>MUCH BETTER</strong></strong> and <strong><strong>MATURER</strong></strong> than &#x201C;Swift in iOS&#x201D; .</p><p>If you have experience on coding Swift in iOS by using Xcode, you should have experienced some really really bad. And I would like share my list of complaints :</p><ul><li><strong><strong>Too many breaking changes on Swift standard API (from Swift 1.0 to 3.0) &#x2014;</strong></strong> Let&#x2019;s wait to see the breaking changes on Swift 4 :p .</li><li><strong><strong>Xcode* keep crashing</strong></strong> &#x2014; boom ! &#x201C;Xcode quit unexpectedly&#x201D;.</li><li><strong><strong>Xcode* keep showing &#x201C;internal error&#x201D;</strong></strong> &#x2014; boom! All syntax-highlight has gone and you can only wait for the Xcode self-recover by making some line changes.</li><li><strong><strong>Xcode* has NO &#x201C;Refactor&#x201D; features for Swift</strong></strong></li><li><strong><strong>Swift Compiler is too weak to work on &#x201C;Type inference&#x201D; </strong></strong>&#x2014; Result in Long compile time. When the expression is too complex and it fails to find the correct Type, compiler will ask you to type it manually. Sometimes, seriously, the compiler will encounter &#x201C;segmentation fault&#x201D; because of Type inference.</li><li><strong><strong>Xcode&#x2019;s* Swift inspection is very slow </strong></strong>&#x2014; The best approach is never wait the Xcode to prompt syntax error, after writing a lot of code and directly press &#x2318;+B to compile and wait the compiler prompt any syntax error you had made.</li><li><strong><strong>Swift SourceKit inspection is not accurate</strong></strong> &#x2014; For example, missing a <code>}</code> of a closture would result in unrelated error message <code>XXX is not convertible to YYY</code> . It is really annoying and confusing, especially for new comers.</li><li><strong><strong>Swift SourceKit inspection is disruptive &#x2014; </strong></strong>Every time when I had made a typo mistake on syntax. i.e. missing a <code>{</code> or <code>}</code> , Xcode mark the line with red and <strong><strong>STOP</strong></strong> doing syntax highlight on the later code or even crash on &#x201C;internal error&#x201D;. It really annoys my normal work flow. I solve this problem by using Intellij App Code which I feel more productive and zero-annoyance when coding some non-UI logics.</li><li><strong><strong>Objective-C syntax is too strange to Swift-Only developer </strong></strong>&#x2014; Many useful third-party libraries still using Objective-C but Swift has grow very fast and I believe &#x201C;Pure Swift iOS Project&#x201D; will solve this problem.</li></ul><p><em><em>*Experienced with Xcode &#x2265; 7.0 &amp;&amp; &#x2264; 8.1, iMac 27&quot;(Retina) 2014 Late i7 32GB Ram</em></em></p><h1 id="kotlin-with-the-best-ide">Kotlin with the Best IDE</h1><p>All of the aforementioned bad parts of &#x201C;Swift in iOS&#x201D; are <strong><strong>NOT</strong></strong> valid to &#x201C;Kotlin@Android Studio&#x201D;. JetBrain has provided a very good IDE support for Kotlin.</p><p>Android Studio (&#x2265; 2.0) with Kotlin:</p><ul><li><strong><strong>Super smooth &amp; stable, nearly NO crash</strong></strong></li><li><strong><strong>Inspection and auto-complete is fast</strong></strong></li><li><strong><strong>Concise and accurate syntax error message</strong></strong></li><li><strong><strong>Compiler is clever enough to work on complex type-inference</strong></strong>(i.e. Nested Lambda)</li><li><strong><strong>Trustworthy Refactoring</strong></strong></li><li><strong><strong>Work with Instant Run</strong></strong></li></ul>]]></content:encoded></item><item><title><![CDATA[Part 2 : Picking Kotlin for Android — Killing Features]]></title><description><![CDATA[Be more productive & Write expressive code & Build better architecture]]></description><link>https://intl.gaplo.tech/picking-kotlin-for-android-part-2/</link><guid isPermaLink="false">5e6df3e223787c76f0676cb4</guid><category><![CDATA[kotlin]]></category><category><![CDATA[android]]></category><category><![CDATA[app]]></category><dc:creator><![CDATA[Gap]]></dc:creator><pubDate>Sat, 08 Apr 2017 13:00:00 GMT</pubDate><media:content url="https://intl.gaplo.tech/content/images/2020/03/1_tcsC49oEgeDK8pacA1YLbw-1.png" medium="image"/><content:encoded><![CDATA[<img src="https://intl.gaplo.tech/content/images/2020/03/1_tcsC49oEgeDK8pacA1YLbw-1.png" alt="Part 2 : Picking Kotlin for Android &#x2014; Killing Features"><p>Welcome to Part 2, this part includes lots of Java vs Kotlin sample code snippets as a show case. Many useful comments/arguments are written in the code block. In order to have better reading experience, I would recommend you to use a large screen to continue to read this article.</p><h1 id="compile-time-null-safe">Compile-time Null Safe</h1><p>&#x201C;Compile-time Null-Safe&#x201D; in Kotlin is a serious and fundamental improvement compared with Java. It solves the infamous null problem in Java and gives a very productive way to write high quality and NPE-less code.</p><p>In Java, every object you created will be assigned it to null by default. It is also known as &#x201C;<a href="https://en.wikipedia.org/wiki/Tony_Hoare?ref=intl.gaplo.tech#Apologies_and_retractions" rel="noopener nofollow">The Billon dollar mistake</a>&#x201D;. Numerous &#x201C;workarounds&#x201D; has been brought out in Java, third-parties library or even in IDE, for example:</p><ul><li><code>@NotNull</code></li><li><code>Objects.requireNonNull(..)</code></li><li><code>Optional.ofNullable(..)</code></li></ul><p>Closely look at the follow Java example, it shows four different ways to write the same functionality.</p><pre><code class="language-java">// Java null risk workaround example

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Optional;
import static java.lang.System.*;

class NullRisk {
  // Bad code in Java
  public String doRiskyThing(String str) {
    return str.toLowerCase();
  }
  
  // Human error by typing wrong annotation, should be @NotNull
  public String doMissLeadingThing(@Nullable String str) {
    return str.toLowerCase(); // IDE warning
  }
  
  // possible work around, IDE will show warning if the input is null
  // but it is safe to compile
  @NotNull
  public String doSafeThing(@NotNull String str){
    return str.toLowerCase();
  }

  // possible work around, but using Optional in java is pain and not handy as Kotlin/Scala
  public Optional&lt;String&gt; doOptionalThing(@Nullable String strOpt) {
    return Optional.ofNullable(strOpt).map(String::toLowerCase);
  }

  {
    // compile OK! throw NullPointerException in run-time
    out.println(doRiskyThing(null));
    
    // compile OK! IDE warning, throw NullPointerException in run-time
    out.println(doSafeThing(null)); 
    
    // compile OK! throw NullPointerException in run-time
    out.println(doMissLeadingThing(null));
    
    // compile OK!
    out.println(doOptionalThing(null));
  }
}</code></pre><p>The first function <code>doRiskyThing</code> is written w/o <code>@NotNull</code> annotation. The function is not self-documented and you must do some workaround to improve it.</p><p><strong><strong>If we focus on the function declaration </strong></strong><code>public String doRiskyThing(String riskyString)</code><strong><strong>without looking at the implementation details</strong></strong>, developers won&#x2019;t get any information about the input parameter. Questions may come up for Java developers :</p><ul><li>Can I pass a <code>null</code>?</li><li>if I pass a <code>null</code> , what will be the return? <code>null</code>? empty string? other default string?</li></ul><p>Consider if developers are required use <code>doRiskyThing</code> to build something, they had to spend extra effort to run a test or look at the source code to build up their confidence.</p><p>The other two examples are the popular &#x201C;Work Arounds&#x201D;, ONLY <code>doOptionalThing(String strOpt)</code> is safe in run-time.</p><p><strong><strong>In Kotlin, nullable object must be represented by appending </strong></strong><code><strong><strong>?</strong></strong></code><strong><strong> </strong></strong>. For example,<code>Int?</code> <code>String?</code> <code>Long?</code> <code>DateTime?</code> <code>BigDecimal?</code> . This syntax will be checked in compile time and also in IDE inspection.</p><p>No workaround is needed, and the follow functions are <strong><strong>by-design</strong></strong> self-documented.</p><pre><code class="language-kotlin">// Kotlin null safe example
// Java null risk example 

fun doSth(str: String): String {
    return str.toLowerCase()
}

fun doSthOptional(str: String?): String? {
    // Optional chainning, similiar to Swift
    return str?.toLowerCase()
}

doSth(null) // compile error
doSth(&quot;Abc&quot;) // return &quot;abc&quot;

doSthOptional(null) // return null
doSthOptional(&quot;Abc&quot;) // return &quot;abc&quot;

// More example on copmile-time checking
var a0 // fail, compile error  
var a1 = null // fail, compile error  
var a2: String = null // fail, compile error  
var a3: String? = null // OK

println(a3) // OK, print null  
println(a3.toLowerCase()) // fail, compile error  
println(a3?.toLowerCase()) // OK, print null</code></pre><p>As you can see the fundamental difference in this tiny example, why don&#x2019;t Java fix &#x201C;the billion dollar mistake&#x201D;?</p><blockquote>Java has to maintain the Backward Compatibility</blockquote><p><strong><strong><em><em>So, you may ask what is the point that related to Kotlin?</em></em></strong></strong></p><p>In 2016 Aug, we are going use existing Java libraries to write a <strong><strong>NEW</strong></strong> Android application or <strong><strong>NEW</strong></strong> backend application. Considering the fact that both of Java &amp; Kotlin can 100% using the existing Java libraries. If we pick Java instead of Kotlin, we have to sacrifice the beauty of compile-time Null-safe and do a lot of extra &#x201C;work-arounds&#x201D; in order to cope with the trade off (Backward compatibility) that we don&#x2019;t even need ?</p><blockquote>Kotlin sounds interesting? Null-Safe is only one of the major killing features.</blockquote><blockquote>There are at least 5+ killing features I am going to introduce.</blockquote><p><strong><strong><em><em>Cautions/Disclaimer:</em></em></strong></strong><em><em> </em></em><strong><strong><em><em>If you keep reading the following Kotlin&#x2019;s killing features, you will never want to go back to Java. Trust me, think twice before you do!</em></em></strong></strong></p><h1 id="zero-overhead-nullable-type-nullable-chaining-and-default-value">Zero Overhead Nullable Type, Nullable Chaining and Default Value</h1><p>Java 8 has introduced a new <code>Optional&lt;T&gt;</code> Class into <code>java.util</code> package. As <code>Optional&lt;T&gt;</code> is just a value type (container) to wrap a value.</p><p>However, the lack of pattern matching in Java (A very power features in Scala), unwrapping and comparing the wrapped value are damn pain in Java. <code><a href="http://stackoverflow.com/questions/31922866/why-should-java-8s-optional-not-be-used-in-arguments?ref=intl.gaplo.tech" rel="noopener nofollow">Optional&lt;T&gt;</a></code><a href="http://stackoverflow.com/questions/31922866/why-should-java-8s-optional-not-be-used-in-arguments?ref=intl.gaplo.tech" rel="noopener nofollow"> is proposed to be used <strong><strong>ONLY</strong></strong> for return type to raise the awareness of &#x201C;no result&#x201D;,</a> hence, avoid NPE. Check to see the following example</p><figure class="kg-card kg-code-card"><pre><code class="language-java">// Optional Type Overhead
Optional&lt;String&gt; strOpt = null; // Compile OK

Optional&lt;String&gt; strOpt = Optional.empty(); // Compile OK

strOpt = null; // Compile OK

strOpt == null; // Compile OK =&gt; true

strOpt = Optional.of(&quot;gary&quot;); // Compile OK

strOpt = Optional.ofNullable(&quot;gary&quot;); // Compile OK

// cannot direct assign a value
strOpt = &quot;gary&quot;; // Compile error: Type mismatch
  
// cannot direct compare with a value
strOpt.equal(&quot;gary&quot;); // Compile error: Type mismatch
  
strOpt.equal(Optional.of(&quot;gary&quot;)); // Compile OK, true
  

// unwrap and compare the wrapped value
if(strOpt.isPresent()) {
  final String str = strOpt.get();
  return str.equal(&quot;gary&quot;);
} 
else {
  return false
}

// do something when the wrapped value is not null
strOpt.ifPresent(str -&gt; {
  doSth(str)
});
  
// Easy to be mis-used
// in some case,
// null is a good representation of nothing instead of empty String
String str = strOpt.orElse(&quot;&quot;);

// throw NullPointerException if the wrapped value is null
String str = strOpt.get();</code></pre><figcaption>Java Optional Overheads example</figcaption></figure><p>Let&#x2019;s see a simple example of Scala pattern matching (FYI):</p><figure class="kg-card kg-code-card"><pre><code class="language-scala">// pattern matching in Option Type
val usernameOpt: Option[String] = Some(&quot;gary&quot;)

usernameOpt match {
    case Some(&quot;gary&quot;) =&gt; println(&quot;matched gary!&quot;)
    case Some(&quot;peter&quot;) =&gt; println(&quot;matched peter!&quot;)
    case Some(username) =&gt; println(&quot;found ${username}, but no username is matched&quot;)
    case None =&gt; println(&quot;No username is found&quot;)
}</code></pre><figcaption>Scala Option pattern matching</figcaption></figure><p>Let&#x2019;s see how powerful is Kotlin&#x2019;s Nullable Type:</p><figure class="kg-card kg-code-card"><pre><code class="language-kotlin">// Kotlin NullableType is different from java.util.Optional or scala.Option
// it is NOT a Value Type

var str: String? = null

str == null // Compile OK! =&gt; true 

str = &quot;gary&quot;  // Compile OK! 

str == null // Compile OK! =&gt; false
str == &quot;gary&quot; // Copmile OK! =&gt; true

// Optional chaining
str?.toUpperCase() == &quot;GARY&quot; // Compile OK! =&gt; true
str?.toLowerCase()?.capitalize() == &quot;Gary&quot; // Compile OK! =&gt; true

// Default value `?:`
val username = str ?: &quot;N/A&quot; // username is String now!

// Kotlin Smart cast - if
if(str is String) {
   // the Type of str is smart casted to `String`!!!
   println(str.toUpperCase())
}

// Kotlin Smart cast - when
when(str) {
  is String -&gt; println(str.toUpperCase()) 
  else -&gt; println(&quot;str is null&quot;)
}</code></pre><figcaption>Kotlin Nullable Type</figcaption></figure><p>From my experience, refactoring in Java/Scala from a Type to java.util.Optional/scala.Option Type is a disaster and seems like rewriting a whole part of the affected codes. But refactoring Kotlin&#x2019;s Nullable Type is nearly zero-effort.</p><h1 id="type-inference">Type Inference</h1><p>Type inference polishes the beauty of static-typed language and can boost the coding productivity to reach as fast as dynamic-typed level.</p><p>Java and Kotlin are both static and strong typing language. This characteristic is very important to large scale application.</p><blockquote>Good Things Come With a Price</blockquote><p>In Java, compared with dynamic and weak typing language like PHP or Javascript, you have to spend extra effort to specify the type definition for variables.</p><p>Sometimes, the type definitions are meaningless and verbose because</p><ul><li>It is too obvious to Human</li><li>The compiler should clever enough to know the type</li><li>Type definitions increase the cost of refactoring.</li></ul><p>For example, when we create a map with seed values with type key: <code>String</code> value: <code>Integer</code></p><figure class="kg-card kg-code-card"><pre><code class="language-java">// Java 9
final Map&lt;String,Integer&gt; abc = Map.of(&quot;a&quot;,1, &quot;b&quot;, 2, &quot;c&quot;, 3);

// Java &gt;= 5
final Map&lt;String,Integer&gt; abc = new HashMap&lt;String, Integer&gt;() {{  
    put(&quot;a&quot;,1);
    put(&quot;b&quot;,2);
    put(&quot;c&quot;,3);
}};

final Integer c = abc.get(&quot;c&quot;);

// Question: 
// If you are asked to refactor from Map&lt;String,Integer&gt; to Map&lt;String,String&gt;,
// try to estimate the efforts!</code></pre><figcaption>Verbose Java</figcaption></figure><p>In Kotlin,</p><blockquote>let the compiler do it for you</blockquote><p>We can skip type definition and let the compiler make type inference for us.</p><pre><code class="language-kotlin">// val abc: Map&lt;String,Int&gt; = mapOf(&quot;a&quot; to 1, &quot;b&quot; to 2, &quot;c&quot; to 3)
// the actual type of the Map is obvious
// thus we can skip Map&lt;String,Int&gt; and let kotlin compiler to make type inference
val abc = mapOf(&quot;a&quot; to 1, &quot;b&quot; to 2, &quot;c&quot; to 3)

val abcd = abc + (&quot;d&quot; to 4) // ok!
val abce = abc + (&quot;e&quot; to &quot;5&quot;) // compile error: Type mismatch

// val c: Int? = abc[&quot;c&quot;]
var c = abc[&quot;c&quot;] 

c = &quot;2&quot; // compile error: Type mismatch

// Same Question: 
// If you are asked to refactor from Map&lt;String,Integer&gt; to Map&lt;String,String&gt;,
// try to estimate the efforts!</code></pre><p>The syntax is very similar to dynamic typed language but any type-mismatched operations would be considered as compilation error.</p><p>Kotlin with type inference can help us to write less-verbose code without sacrifice anything.</p><h1 id="default-argument-named-argument">Default Argument &amp; Named Argument</h1><p>In a real world, there exist some situations we damn need default value instead of null. Java developers can either do a very ugly work around if-null-then-default value or do a verbose method overloading. For example,</p><figure class="kg-card kg-code-card"><pre><code class="language-java">// Ugly work around to save time (compared to method overload)
void doSomething(
  @NotNull String fname,
  @NotNull String lname,
  @Nullable String addr,
  @Nullable Gender gender
) {  
  requireNonNull(fname);
  requireNonNull(lname);
  if(addr == null) addr = &quot;N/A&quot;;
  if(gender == null) gender = Gender.Unknown;
  // do sth
}
String lastName = &quot;lastName&quot;;
String firstName = &quot;firstName&quot;;
String gender = Gender.Male;
String address = &quot;address&quot;;

// As we know the implementation will provide default value for null
// so we type null in the parameter...
// code-smell? WTF by new comer?
// Question: What is the null suppose to be?
doSomething(firstName,lastName,null,null);  
doSomething(firstName,lastName,address,null);  
doSomething(firstName,lastName,null,gender);  
doSomething(firstName,lastName,address,gender);

// without named paramenter...
// Can you notify the bug when you type out this?
// it is safe to compile but logically error 
// bug: lastName and firstName are swapped
doSomething(lastName,firstName,address, gender); 

// To avoid null and expose a better API
// without default and namged argument, method overloading is the only way
void doSomething(String fname, String lname) {
  doSomething(fname,lname,&quot;N/A&quot;, Gender.Unknown);
}
void doSomething(String fname, String lname, String addr) {
  doSomething(fname,lname,addr, Gender.Unknown);
}
void doSomething(String fname, String lname, Gender gender) {
  doSomething(fname,lname,&quot;N/A&quot;, gender);
}
void doSomething(String fname, String lname, String addr, Gender gender) {
  // do sth
}

// Question: 
// if you asked to add one more arugment let say &quot;Phone&quot;
// how many overloading is needed?
  </code></pre><figcaption>Without Named &amp; Default Arguments</figcaption></figure><p>Either the &#x201C;ugly work around&#x201D; and the &#x201C;verbose method overloading&#x201D; are also not self-documented, hard to refactor/change in Java.</p><p>Kotlin support <strong><strong>Named and Default Argument</strong></strong>, it helps developers to create a more intuitive, meaningful and maintainable API.</p><figure class="kg-card kg-code-card"><pre><code class="language-kotlin">// Default argument
fun doSomething(
  fname: String,
  lname: String,
  addr: String = &quot;N/A&quot;,
  gender: Gender = Gender.Unknown
) {
  // do something that really need fname,lname, addr, gender..
}

val firstName = &quot;firstName&quot;
val lastName = &quot;lastName&quot;
val address = &quot;Address&quot;
val gender = Gender.Male

// Normally you can do this
doSomething(firstName, lastName, address, gender) // OK!
doSomething(firstName, lastName, address)  // OK!
doSomething(firstName, lastName)  // OK!

// Fail, compile error! Type mismatch! Expected String type
doSomething(firstName, lastName, gender) 

// It can be solved by Named Argument
// with Named Argument
doSomething(
  fname = firstName,
  lname = lastName,
  addr = address,
  gender = gender
) // OK!

// skip the address (3rd argument)
doSomething(
  fname = firstName,
  lname = lastName,
  gender = gender
) // OK!

// Order is not important!!
doSomething(
  addr = address,
  lname = lastName,
  fname = firstName
) // OK!

// Fail! compile error: lname is required
// because lname has no default value
doSomething(
  addr = address,
  fname = firstName
)

// Ask yourself:
// Is it possible to achieve the same level of conciseness in Java?</code></pre><figcaption>Named &amp; Default Arguments</figcaption></figure><p>As the gist example has clearly demonstrate the power of <strong><strong>Named and Default Argument. </strong></strong>This is a fundamental and serious improvement in Kotlin compared with Java.</p><p>Once you get used to the Kotlin&#x2019;s powerful <strong><strong>Named and Default Argument </strong></strong>to design your interface<strong><strong>, </strong></strong>you won&#x2019;t go back to Java.</p><h1 id="extension-is-awesome">Extension is Awesome</h1><p>In Java, we can not directly add functionalities on a <code>Class</code>. If some logics applied on a specific <code>Type</code> and have to be used multiple time in a project, Java developers get used to create a <code>XXXUtils</code> for <code>Type XXX</code> in order to follow the D.R.Y. principle.</p><blockquote>Programming has no magic. All about Abstractions</blockquote><p>Kotlin provided a much better abstraction to model the similar problems that are solved by &#x201C;Java Utils Pattern&#x201D; &#x2014; <strong><strong>Extension</strong></strong></p><p>Traditional Java Utils usage</p><figure class="kg-card kg-code-card"><pre><code class="language-java">// please focus on the function name &amp; declaration 

// As we all know, String is a final class and we can&apos;t extend
class StringUtils {
  public static TypeA toTypeA(String str)
}

// Assume TypeA is from third party library that you can&apos;t extend
class TypeAUtils {
  public static Double calculateResult(TypeA a) 
}

// Similar to String, you can&apos;t extend Double
class DoubleUtils {
  public static Double toTwoDecimalPlace(Double d) 
}

String someString = &quot;someString&quot;;

// The execution order is not as same as to our reading direction(top-to-bottom)
// it is difficult to read
final Double typeAResultWithRounding = 
  DoubleUtils.toTwoDecimalPlace(
    TypeAUtils.calculateResult(
      StringUtils.toTypeA(someString)
    )
  );

// flatten it?  better to read but more unnessecary variables are created
final TypeA typeA = StringUtils.toTypeA(someString);
final Double typeAResult = TypeAUtils.calculateResult(typeA);
final Double typeAResultWithRounding = DoubleUtils.toTwoDecimalPlace(typeArResult);
  
// Question: 
// How do a new comer would know to use exactly these three Utils?
// Normally, they would ask someone or implement a duplicate one for their use case.</code></pre><figcaption>Utils Pattern</figcaption></figure><p>How is Kotlin provided a better abstraction for this problem? We can directly add an &#x201C;Extension&#x201D; for a specific <code>Type</code> with a very simple syntax! (Very similar to javascript)<br></p><figure class="kg-card kg-code-card"><pre><code class="language-kotlin">
// Kotlin

// extension, give toTypeA() functionality to String
fun String.toTypeA(): TypeA { 
  return TypeA(str = this)
}

// give calculateResult() functionality to TypeA 
fun TypeA.calculateResult(): Double {
  return this.str.length + 0.123456789
}

// give toTwoDecimalPlace() functionality to Double
fun Double.toTwoDecimalPlace(): Double {
  return BigDecimal(this).setScale(2, RoundingMode.HALF_UP).toDouble()
}
val someString = &quot;someString&quot;

// Better than Utils pattern
// 1. By-design chainning from top-to-bottom which is better for reading
// 2. Extension are statically import(Unlike Swift), extension usages are tracable.
// 3. IDE autocomplete works well to identify extensions on a Type,
val typeAResultwithRounding = someString
                                .toTypeA()
                                .calculateResult()
                                .toTwoDecimalPlace()

// Even new comer would see the extra functionality in the IDE auto-complete list!!</code></pre><figcaption>Kotlin Extensions</figcaption></figure><p>&#x201C;Java Utils Pattern&#x201D; is not IDE-friendly, new comer won&#x2019;t notice some functionalities are already implemented in the XXXUtils while Kotlin&#x2019;s Extension is completely solved this problem.</p><p>Here are some screenshots to demonstrate the IDE auto-complete with the Kotlin extension</p><figure class="kg-card kg-image-card"><img src="https://intl.gaplo.tech/content/images/2020/03/1_aTx_tV7pMMxizzvRm0mScQ.png" class="kg-image" alt="Part 2 : Picking Kotlin for Android &#x2014; Killing Features" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://intl.gaplo.tech/content/images/2020/03/1_UWzz4js4yI3MFt81kh1ySg.png" class="kg-image" alt="Part 2 : Picking Kotlin for Android &#x2014; Killing Features" loading="lazy"></figure><p>With Kotlin extension, you would never have to ask</p><blockquote><em>Is that XXXUtils.someFunc() is implemented already?</em></blockquote><p>and never have to search in the .utils folder. 100% of time you will find it in the auto-completed list.</p><h1 id="handy-data-class">Handy Data Class</h1><p>In a strongly-typed language, we will create numerous &#x201C;Value Object&#x201D; to hold data by a explicit type to classify their domain. For example, API Response, API Request, Data Model, Proxy Object, etc.</p><p>It is a very common practice and should be as simple as possible.</p><p>After I have met with Scala and recently with Kotlin. The only Java impression on creating a &#x201C;Value Object&#x201D;:</p><blockquote>Java is too verbose and too costly to be used for Functional Programming (Immutable approach)</blockquote><p>Start reading the following Kotlin <code>data class</code> to get some insight and don&#x2019;t forget to read the comment :) .</p><figure class="kg-card kg-code-card"><pre><code class="language-kotlin">data class User(
  // primary construtor
  val id: UUID,
  val username: String,
  val firstName: String? = null,
  val lastName: String? = null,
  val address: String? = null,
  val isEmailVerified: Boolean = false
) {
  // getter only
  var fullName: String get() {
    return &quot;${firstName} ${lastName}&quot;
  }

  // a function inside a User
  fun doSth() {
    println(&quot;do something&quot;)
  }
}

// By default, Kotlin has generated very useful constructor for a data class
// with the power of the Named and Default Argument
// We can use it in the following ways

// firstName, lastName, address, isEmailVerified is not required
User(
  id = UUID.randomUUID(),
  username = &quot;garylo123&quot;
)

// but you can still override the default value
val gary = User(
  id = UUID.randomUUID(),
  username = &quot;garylo123&quot;,
  firstName = &quot;Gary&quot;,
  lastName = &quot;LO&quot;,
  address = &quot;HK Somewhere&quot;
)

println(gary.username) // print &quot;garylo123&quot;
println(gary.fullName) // print &quot;Gary LO&quot;

gary.doSth() // print &quot;do something&quot;

// developer-friendly for debuging
println(gary) // print &quot;User(id=229c7b64-7dd3-4b5f-80bd-c88dc15ed13e, username=garylo123, firstName=Gary, lastName=LO, address=HK Somewhere, isEmailVerified=false)&quot;



// Data class provide a handy function .copy for pure immutable object
// if we want to update the address of `gary`, we create a new copy
// original gary won&apos;t be mutated 
val updatedUser = gary.copy(address = &quot;Hong Kong Somewhere&quot;)</code></pre><figcaption>Kotlin Data Class</figcaption></figure><p>In Java, developers have to use comparatively poor way (mutable or builder pattern) to create an complex Object. For example,<br></p><figure class="kg-card kg-code-card"><pre><code class="language-java">// Only used for holding the Data. 
class MutableJavaDataObject {
  @NotNull UUID id;
  @NotNull String username;
  @Nullable String firstName;
  @Nullable String lastName;
  @Nullable String address;
  bool isEmailVerified = false
}

// Problem?
// 1. All properties are mutable
// 2. No Constructor, how do others know `id` and `username` should NOT be null?
// 3. In Java, One file can only contain one Class.
//    Even a class is just a few line....</code></pre><figcaption>Mutable Java Objects</figcaption></figure><p>How we use it in Java ?</p><figure class="kg-card kg-code-card"><pre><code class="language-java">// MutableJavaDataObject, https://gist.github.com/gaplo917/deee7a16bf9f60c69cfedb47884820fa

// created by empty contructor
MutableJavaDataObject mjdo = new MutableJavaDataObject();

// assign the value directly
mjdo.id = UUID.randomUUID();
mjdo.address = &quot;Hong Kong&quot;;

// printing the class reference is useless in 99% of time
System.out.print(mjdo) // print &quot;MutableJavaDataObject@xxxxxxx&quot;

// bugs? missed to assign the mjdo.username but it is compile time safe


// Question:
// A safer way? Builder pattern? Seriously!? for every data object? 

// Using Kotlin would cost you the same amount of time 
// that you write MutableJavaDataObject (work around),
// but gives much cleaner, robuster and safer `data class`</code></pre><figcaption>Mutable Java Objects usaged</figcaption></figure><p>Kotlin provides a very handy <code>data class</code> that</p><ul><li>leverage the power of <strong><strong>Name and Default Argument </strong></strong>on constructor</li><li>provide handy <code>.copy</code> for writing pure immutable object</li><li>is developer-friendly for debugging</li></ul><p>It helps to reduce the verbosity of getter, setter and multiple constructor. What&#x2019;s more, <code>data class</code> is by-default ready to be used in some popular JSON Serialization library such as GSON, Jackson.</p><p>If you are familiar with Java, without <strong><strong>Name and Default Argument</strong></strong>, you know that it is not feasible to create a &#x201C;Value Object&#x201D; that can achieve the same level of convenience as Kotlin&apos;s <code>data class</code> does.</p><h1 id="-kotlinize-java-library">&#x201C;Kotlinize&#x201D; Java Library</h1><p>In a large scale application, consistency is the most important role to keep source codes more predictable and maintainable.</p><p>By default, Kotlin won&#x2019;t encourage to create getter/setter function for a variable. Each variable has a get()/set(value) to implement:</p><figure class="kg-card kg-code-card"><pre><code class="language-kotlin">class User {
    var name: String? = null
      get() {
        println(&quot;getting a username = ${field}&quot;)
        return field
      }
      set(nName) {
        println(&quot;changing to a new username ${nName} from ${field}&quot;)
        field = nName 
      }
  
}

fun main() {
    val user = User()
    
    user.name = &quot;Gary LO&quot;
    
    println(&quot;We get the username = ${user.name}&quot;)
    
    // Expected console output: 
    // changing to a new username Gary LO from null
    // getting a username = Gary LO
    // We get the username = Gary LO
}</code></pre><figcaption>Kotlin Getter/Setters</figcaption></figure><p>Therefore, in pure Kotlin code we never write<code>getXXX()</code> or <code>setXXX(value)</code>. But, nearly 100% of Java library would use getter/setter.</p><p><strong><strong><em><em>How does Kotlin solve this discrepancy? Let see the &#x201C;TextView&#x201D; in Java Android library.</em></em></strong></strong></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://intl.gaplo.tech/content/images/2020/03/1_xLockNoj0fV-6KK2fQivhA.png" class="kg-image" alt="Part 2 : Picking Kotlin for Android &#x2014; Killing Features" loading="lazy"><figcaption>Standard Android TextView(Java) in Kotlin</figcaption></figure><p>Kotlin will Kotlinize the Java &#x201C;default&#x201D; getter/setter into Kotlin style, you would never feel uncomfortable to use Java libraries.</p><p>If you are experience in using Java libraries in Scala, you must damn want this features, at least I do lol.</p><h1 id="better-functional-programming-support">Better Functional Programming Support</h1><p>Writing functional style code in Java is comparatively much more expensive than in Kotlin. Because Kotlin provided the following features:</p><ul><li>Type Inference</li><li>Inexpensive Data Class (Immutable <code>.copy</code> features)</li><li>Intuitive Type declaration for Functions/Lamdas, i.e.<code>(Int)-&gt; Int</code> , <code>(Int,Int) -&gt; Bool</code> instead of <code>java.util.functions.BiFunction&lt;T,U,R&gt;...</code></li><li>Support Destructing</li><li>Every thing is an expression (easier to write declarative code)</li></ul><figure class="kg-card kg-code-card"><pre><code class="language-kotlin">// `to` is a infix function to create a Pair&lt;L,R&gt;
val pair = 1 to &quot;one&quot;

// decompose the pair
val (num,str) = pair</code></pre><figcaption>Easy to create Pair &amp; destructing a pair</figcaption></figure><figure class="kg-card kg-code-card"><pre><code class="language-kotlin">val list = listOf(1,2,3,4,5)

// destructing the list
val (a,b,c) = list

println(&quot;a = $a, b = $b, c = $c&quot;) 
// a = 1, b = 2, c = 3

// already include in kotlin std-lib
val head = list.head
val tail = list.tail

println(head) // 1
println(tail) // [2,3,4,5]</code></pre><figcaption>Destructing a List</figcaption></figure><figure class="kg-card kg-code-card"><pre><code class="language-kotlin">data class User(val id: Int, val name: String, val address: String)

val user = User(id = 1, name = &quot;GARY LO&quot;, address = &quot;Hong Kong&quot;)

// Destructing order is import!!
val (userId, userName, userAddress) = user

println(&quot;id = $userId, name = $userName, address = $userAddress&quot;)
// id = 1, name = GARY LO, address = Hong Kong</code></pre><figcaption>Destructing a data class</figcaption></figure><figure class="kg-card kg-code-card"><pre><code class="language-kotlin">val list = listOf(1,2,3,4,5)

// destructing the list
val (a,b,c) = list

println(&quot;a = $a, b = $b, c = $c&quot;) 
// a = 1, b = 2, c = 3

// already include in kotlin std-lib
val head = list.head
val tail = list.tail

println(head) // 1
println(tail) // [2,3,4,5]</code></pre><figcaption>Destructing a List</figcaption></figure><figure class="kg-card kg-code-card"><pre><code class="language-kotlin">// Kotlin - currying function example

fun doCurrying(first: Int): (Int) -&gt; ((Int, Int) -&gt; Int) -&gt; Int {
  return { second -&gt; { f -&gt; f(first,second) } }
}

val add = { a: Int, b: Int -&gt; a + b }
val multiply = { a: Int, b: Int -&gt; a * b }
val minus = { a: Int, b: Int -&gt; a - b }

val curriedFour = doCurrying(4)
val curriedFourFive = curriedFour(5)

curriedFourFive(add) // 9
curriedFourFive(multiply) // 20
curriedFourFive(minus) // -1
</code></pre><figcaption>Currying function example</figcaption></figure><figure class="kg-card kg-code-card"><pre><code class="language-kotlin">// Every Thing is an expression
val input = 1

val result = if(input == 1) &quot;Equal to one&quot; else &quot;Not Equal to One&quot;

val result2 = when(input) {
   1 -&gt; &quot;Equal to one&quot;
   else -&gt; &quot;Not equal to one&quot;
}

val result3 = try {
    input / 0
    &quot;Can be calculated&quot;
  } catch(e: ArithmeticException) {
    &quot;Can&apos;t be calculated&quot;
  }

println(result)  // Equal to one
println(result2) // Equal to one
println(result3) // Can&apos;t be calculated</code></pre><figcaption>Every thing is an expression</figcaption></figure><h1 id="major-goodies-aforementioned-">Major goodies (aforementioned)</h1><ul><li>Compile-time Null Safe</li><li>Zero Overhead Nullable Type, Nullable Chaining and Default Value</li><li>Type Inference</li><li>Default Argument &amp; Named Argument</li><li>Extension is Awesome</li><li>Handy Data Class</li><li>&#x201C;Kotlinize&#x201D; Java Library</li><li>Better Functional Programming Support</li></ul><h1 id="minor-goodies">Minor goodies</h1><ul><li>Intuitive String Template</li><li>Lots of handy function are implemented in Kotlin std-lib i.e. <code>array.find</code> &#x2026;</li><li>Multiple class or object in single file</li><li>Support legacy JVM. Kotlin Runs on Java 6 and its interface can have default implementation just like in Java 8.</li><li>Best IDE Support</li></ul><p><strong><strong><em><em>Next &#x2014; </em></em></strong></strong><a href="https://medium.com/@gaplotech/part-3-picking-kotlin-for-android-swift-in-android-7db405962c13?ref=intl.gaplo.tech" rel="noopener"><em><em>Part 3 : Picking Kotlin for Android &#x2014; Swift in Android?</em></em></a><br></p>]]></content:encoded></item><item><title><![CDATA[Part 1 : Picking Kotlin for Android — The Reason Behind]]></title><description><![CDATA[Picking a new language for android development requires strong reasons. Right?]]></description><link>https://intl.gaplo.tech/picking-kotlin-for-android-part-1/</link><guid isPermaLink="false">5e6df17d23787c76f0676c90</guid><category><![CDATA[kotlin]]></category><category><![CDATA[android]]></category><category><![CDATA[app]]></category><dc:creator><![CDATA[Gap]]></dc:creator><pubDate>Sat, 08 Apr 2017 12:00:00 GMT</pubDate><media:content url="https://intl.gaplo.tech/content/images/2020/03/1_tcsC49oEgeDK8pacA1YLbw.png" medium="image"/><content:encoded><![CDATA[<h1 id="overview">Overview</h1><img src="https://intl.gaplo.tech/content/images/2020/03/1_tcsC49oEgeDK8pacA1YLbw.png" alt="Part 1 : Picking Kotlin for Android &#x2014; The Reason Behind"><p>This is a long article and will be divided into four parts:</p><ul><li>Introduction of Kotlin &amp; The Reasons behind the Choice</li><li>Kotlin&#x2019;s Killing Features (with details Java comparison)</li><li>How is Kotlin in Android Development compared to Swift Development in iOS?</li><li>Kotlin Pro Tips and Our Kotlin Use Case sharing on Android Development (To be continued)</li></ul><h1 id="who-am-i">Who am I ?</h1><p>I am a senior mobile developer in <a href="https://www.linkedin.com/company/harborx/about/?ref=intl.gaplo.tech">Harborx</a>. Harborx targets to build a simplified forex trading platform which provide fun, smooth and intuitive trading experience for everyone.</p><p>Before I had met with &#x2764; Kotlin, I have a rich experience with Java &amp; Scala. Every time when a new comer asked me to compare Java with Scala, there are some &#x201C;Java impressions&#x201D; straightly come to my head:</p><ul><li>Java is too easy and verbose</li><li>Null Pointer Exception (NPE, <a href="https://en.wikipedia.org/wiki/Tony_Hoare?ref=intl.gaplo.tech#Apologies_and_retractions" rel="noopener nofollow">The Billon dollar mistake</a>)</li><li>Java 8 is better. But far from enough</li><li>Writing functional style code in Java is comparatively costly &#x2014; Difficult to use immutable object, no tuples, no type inference, clueless <code>Function&lt;T,R&gt;</code> , <code>BiFunction&lt;T,U,R&gt;</code> , <code>Consumer&lt;T&gt;</code> &#x2026; , default mutable Collection, Map, Set.</li></ul><p>In 2016 Aug, we started to build a <a href="https://play.google.com/store/apps/details?id=com.harborx.app.android&amp;ref=intl.gaplo.tech" rel="noopener nofollow">new Harborx Android app</a>. It is written by 100% Kotlin with Java Library.</p><figure class="kg-card kg-image-card"><img src="https://intl.gaplo.tech/content/images/2020/03/harborx-android-playstore.png" class="kg-image" alt="Part 1 : Picking Kotlin for Android &#x2014; The Reason Behind" loading="lazy"></figure><p>After that, I think these &#x201C;Java impressions&#x201D; are still valid when comparing Kotlin to Java.</p><h1 id="kotlin-introduction">Kotlin Introduction</h1><p>Kotlin is a relatively new and statically typed programming language for the JVM, Android and the browser. It is backed by JetBrains &#x2014;The biggest IDE manufacturer in the world.</p><h2 id="brief-timeline">Brief Timeline</h2><ul><li>In 2010, JetBrains started Kotlin Project.</li><li>In 2011, JetBrains has clearly state their vision &#x2014; <a href="https://blog.jetbrains.com/kotlin/2011/08/why-jetbrains-needs-kotlin/?ref=intl.gaplo.tech" rel="noopener nofollow">Why JetBrains needs Kotlin</a>.</li><li>In 2012, JetBrains has make a first public Kotlin 0.x release.</li><li>In 2013, <a href="https://blog.jetbrains.com/kotlin/2013/08/working-with-kotlin-in-android-studio/?ref=intl.gaplo.tech" rel="noopener nofollow">JetBrains support Kotlin in Android Studio</a></li><li>In 2016 Feb, <a href="https://blog.jetbrains.com/kotlin/2016/02/kotlin-1-0-released-pragmatic-language-for-jvm-and-android/?ref=intl.gaplo.tech" rel="noopener nofollow">Kotlin 1.0 was officially released</a>.</li><li>In 2016 May, <a href="https://blog.gradle.org/kotlin-meets-gradle?ref=intl.gaplo.tech" rel="noopener nofollow">Kotlin meets gradle</a></li><li>In 2017 Mar, <a href="https://blog.jetbrains.com/kotlin/2017/03/kotlin-1-1/?ref=intl.gaplo.tech" rel="noopener nofollow">Kotlin 1.1 was officially released (with revolutionary coroutines features in preview).</a></li><li>In 2017 Apr, <a href="https://blog.jetbrains.com/kotlin/2017/04/kotlinnative-tech-preview-kotlin-without-a-vm/?ref=intl.gaplo.tech" rel="noopener nofollow">Kotlin/Native Tech Preview: Kotlin without a VM</a></li></ul><h2 id="interesting-facts">Interesting Facts</h2><ul><li><strong><strong>JetBrains Official supports Kotlin for Android development.</strong></strong></li><li><strong><strong>JetBrains claims that Kotlin is a programming language built for industrial instead of general purpose. </strong></strong>The basic difference should more likely about thinking of <em><em>&#x201C;is this function commonly used in software industrial ? &#x201D; </em></em>instead of <em><em>&#x201C;does this function increase the size of std-lib ?&#x201D;.</em></em><br>For example, some useful functions like <code>string.replace, collection.find, collection.filterNotNull,</code>are provided in Kotlin standard library while seldom provided in general purpose programming language like Swift and Java.</li></ul><pre><code class="language-kotlin">// Kotlin Standard Library provided

String.replace(
    oldChar: Char, 
    newChar: Char, 
    ignoreCase: Boolean = false
): String

// directly return a object in a collection 
// which is matched with the predicate
fun &lt;T&gt; Iterable.find(predicate:(T) -&gt; Boolean): T?

// filter all null object in the collections
fun &lt;T: Any&gt; Iterable&lt;T?&gt;.filterNotNull(): List&lt;T&gt;</code></pre><ul><li><strong><strong>Kotlin generate JVM byte code instead of source-to-source transpile to Java </strong></strong>&#x2014; No performance degradation if the generated byte code of Java &amp; Kotlin are the same.</li><li><strong><strong>Kotlin 1.1 support from Java 1.6</strong></strong> &#x2014; Most of the legacy Android or Backend Project are supported.</li><li><strong><strong>&#x201C;Method Count on a Hello World Android Project(Disable Proguard)&#x201D;*</strong></strong> &#x2014; Java(17,416), Kotlin 1.1.1(23,587), Scala 2.11(67,608)</li><li><strong><strong>&#x201C;Method Count on a Hello World Android Project(Enable Proguard)&#x201D;</strong></strong> * &#x2014; Java (7,608), Kotlin 1.1.1(7,656), Scala 2.11(11,671),</li><li><strong><strong>&#x201C;Compile Time&#x201D; of Kotlin is much faster than Scala/Groovy and very close to Java*</strong></strong></li></ul><p>* Thanks to <a href="https://github.com/SidneyXu/AndroidDemoIn4Languages?ref=intl.gaplo.tech" rel="noopener nofollow">https://github.com/SidneyXu/AndroidDemoIn4Languages</a></p><h1 id="the-reasons-behind-the-choice">The Reasons behind the Choice</h1><ol><li><strong><strong>The belief of </strong></strong><a href="http://reactivex.io/?ref=intl.gaplo.tech" rel="noopener nofollow"><strong><strong>ReactiveX</strong></strong></a><strong><strong>. </strong></strong>ReactiveX is a combination of the best ideas from the Observer pattern, the Iterator pattern, and functional programming. After spending two month of learning, researching and playing toy projects with RxJava and functional, we compared side-by-side with Java implementation. we found that Kotlin can fundamentally provide a much more intuitive and inexpensive way than Java on writing functional style codes, so that we can maximize the power of ReactiveX.</li><li><strong><strong>The belief of one of the best Android developer in the world </strong></strong>&#x2014; <a href="https://medium.com/u/8ddd94878165?source=post_page-----834e9bc828f3----------------------" rel="noopener">Jake Wharton</a>. If you have gone through a lot of serious Android development, you must know this man (in Harborx, we use Cantonese &#x201C;Jake &#x5927;&#x795E;&#x201D; to represent his name lol, &#x201C;&#x5927;&#x795E;&#x201D; in english is likely mean &#x201C;Master&#x201D;). Jake built a lot of high quality libraries for Android such as ButterKnife, Retrofit, okHttp, RxAndroid, RxBinding, Timber, etc. You can easily find his public Kotlin and Android talk on YouTube/Google plus/Twitter.</li><li><strong><strong>Kotlin has already reached the <em><em>1.0.0</em></em> milestone</strong></strong>. In 2016 Aug, We expect <em><em>1.x.x</em></em> version API is stable for production. It is the right time to start!</li><li><strong><strong>Learning curve of Kotlin is not high. </strong></strong>Picking up Kotlin by a traditional Java developer should be less than 2 week.</li><li><strong><strong>Swift is very similar to Kotlin. </strong></strong>We can nearly apply the same abstraction (Data structures, Extension, Rx&#x2026;) to model the business flow<strong><strong>. </strong></strong>Rewriting same business logics between Android &amp; iOS platform become easier and faster. In contrast, switching from Swift to Java would be very painful. For example, Swift developers usually use a lot of extensions to power up existing class while Java developers use &#x201C;Utils&#x201D; patterns and inheritance to give extra functionality on existing class.</li></ol><p><strong><strong><em><em>Next &#x2014;</em></em></strong></strong><a href="https://intl.gaplo.tech/picking-kotlin-for-android-part-2"><strong><strong><em><em> </em></em></strong></strong><em><em>Part 2 : Picking Kotlin for Android &#x2014; Killing Features</em></em></a></p>]]></content:encoded></item></channel></rss>