<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Dorla Hutch</title>
    <description>The latest articles on DEV Community by Dorla Hutch (@dorla).</description>
    <link>https://dev.to/dorla</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2310158%2F04fd3fbd-69c5-43b8-91a7-7f01c5f084c0.png</url>
      <title>DEV Community: Dorla Hutch</title>
      <link>https://dev.to/dorla</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dorla"/>
    <language>en</language>
    <item>
      <title>Using Loose Equality in JS/TS</title>
      <dc:creator>Dorla Hutch</dc:creator>
      <pubDate>Sat, 10 May 2025 15:50:57 +0000</pubDate>
      <link>https://dev.to/dorla/using-loose-equality-in-jsts-2f52</link>
      <guid>https://dev.to/dorla/using-loose-equality-in-jsts-2f52</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxyemf7x0asfgwpdxsvpv.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxyemf7x0asfgwpdxsvpv.jpg" alt="mirrors showing multiple different equalities" width="722" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;-- Loose Equality allows different kinds to become an equal kind.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I've been feeling, blogposts treat loose equality in JavaScript as a kind of black magic because people do not master it. It isn't supposed to be esoteric magic.&lt;/p&gt;

&lt;p&gt;I am not involved in browser development but I conducted my tests with Firefox and Chrome and I read the MDN pages. You can check the examples.&lt;/p&gt;

&lt;p&gt;I say, make full potential of Javascript's capabilities, intentionally and knowingly. It can save us from some coding overhead, even when the original definition is sometimes flawed. The loose equality can make code more concise…&lt;br&gt;&lt;br&gt;
This means, if you know the types of the operands. Therefore, the loose equality is particularly useful with TypeScript, because it can show you the types of expressions.&lt;/p&gt;

&lt;p&gt;True, even TypeScript lacks understanding of loose equality. TypeScript blatantly lies when it says &lt;code&gt;0 == []&lt;/code&gt; would always return &lt;code&gt;false&lt;/code&gt; (because the opposite is true).&lt;/p&gt;

&lt;p&gt;Let's dive in.&lt;/p&gt;
&lt;h3&gt;
  
  
  Loose comparisons in Typescript
&lt;/h3&gt;

&lt;p&gt;We have two options to use loose equality in TypeScript. 1ˢᵗ We can suppress type checking on operands&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;neverTrue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;neverTrue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or use the "non-null assertion" operator &lt;code&gt;!&lt;/code&gt; on nullable values to turn off errors or warnings while using the loose equality.&lt;/p&gt;

&lt;p&gt;Or 2ⁿᵈ, we can use explicit type casts such as&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;neverTrue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;neverTrue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once both sides use the same type, you can just use &lt;code&gt;==&lt;/code&gt; or &lt;code&gt;!=&lt;/code&gt;. Misuse is hard to miss in TypeScript. TypeScript complains when you are using loose equality not like a strict equality.&lt;/p&gt;

&lt;p&gt;For type coercions, Web Devs might know using the (unary) plus &lt;code&gt;+&lt;/code&gt; for number coercion or &lt;code&gt;!!&lt;/code&gt; (like &lt;code&gt;Boolean()&lt;/code&gt;) or &lt;code&gt;""+&lt;/code&gt; (like &lt;code&gt;String()&lt;/code&gt;). You can cast floating point numbers to integers with specific arithmetic operations such as &lt;code&gt;0 |&lt;/code&gt;, &lt;code&gt;-1 &amp;amp;&lt;/code&gt;, &lt;code&gt;~&lt;/code&gt;, &lt;code&gt;&amp;lt;&amp;lt; 0&lt;/code&gt; or &lt;code&gt;&amp;gt;&amp;gt; 0&lt;/code&gt; instead of using &lt;code&gt;Math.floor()&lt;/code&gt;. Coercing Symbols has always ended in a TypeError in my tests.&lt;/p&gt;

&lt;p&gt;Note: MDN distinguishes the "number coercion" with &lt;code&gt;+&lt;/code&gt; from "numercial coercion" with &lt;code&gt;Number()&lt;/code&gt;, because &lt;code&gt;+&lt;/code&gt; (only the unary operator) does not work with &lt;code&gt;BigInt&lt;/code&gt;. This is a "Big Fail", even the unary minus works! &lt;code&gt;[]+&lt;/code&gt; works just the way as &lt;code&gt;""+&lt;/code&gt; does because &lt;code&gt;[]&lt;/code&gt; is coerced to an empty string first.&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;For your tl;dr&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;equality&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isNaN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nf"&gt;isNaN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;boolean&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;boolean&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;equality&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;JavaScript also features &lt;code&gt;BigInt&lt;/code&gt;s but for brevity I ignore them. We can treat them as if they were numbers which have no comma, infinity and NaN… and no unary plus.&lt;/p&gt;

&lt;p&gt;Technically, by &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Equality" rel="noopener noreferrer"&gt;definition of the MDN pages&lt;/a&gt;, the coercion&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;first tries &lt;code&gt;[Symbol.toPrimitive](&lt;/code&gt;&amp;lt;type-name&amp;gt;&lt;code&gt;)&lt;/code&gt; (for objects, arrays, functions)&lt;/li&gt;
&lt;li&gt;else tries &lt;code&gt;.valueOf()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;else tries &lt;code&gt;.toString()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;but two strings will always be compared literally (byte by byte).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Comparing equal types
&lt;/h3&gt;

&lt;p&gt;Let's begin with a note, that Typescript treats &lt;code&gt;==&lt;/code&gt; pretty much like &lt;code&gt;===&lt;/code&gt; except for ignoring type errors about &lt;code&gt;null&lt;/code&gt;s and &lt;code&gt;undefined&lt;/code&gt;s.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;==&lt;/code&gt; is the same as &lt;code&gt;===&lt;/code&gt; when both operands are of the same type, for all distinguished JavaScript types.&lt;/p&gt;

&lt;p&gt;This comparison is especially strict among objects, functions and symbols.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Remember: arrays and sets and &lt;code&gt;null&lt;/code&gt; are also objects!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Two objects, functions or symbols only satisfy &lt;code&gt;==&lt;/code&gt; or &lt;code&gt;===&lt;/code&gt; when they are the &lt;strong&gt;same&lt;/strong&gt;! "The same" means, any changes made to one side would also be at the other side of the equation at the same time. In the equation, there is only one single value compared with itself.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;undefined&lt;/code&gt; has its own special type. &lt;code&gt;null&lt;/code&gt; is just an object constant without any properties. (There is only ever one constant &lt;code&gt;null&lt;/code&gt; object.)&lt;/p&gt;

&lt;p&gt;It goes as far as leading to &lt;code&gt;{} !== {}&lt;/code&gt; and &lt;code&gt;[] !== []&lt;/code&gt;. Both sides of the equality sign are equal, but separate objects.&lt;/p&gt;

&lt;h3&gt;
  
  
  Comparing distinct types
&lt;/h3&gt;

&lt;p&gt;This is actually the interesting part where we can avoid writing code.&lt;/p&gt;

&lt;p&gt;When I experiment in the browser console with different test cases, this image emerges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;operands of boolean type are replaced by 0 or 1&lt;/strong&gt;, &lt;code&gt;false&lt;/code&gt; is compared as 0, &lt;code&gt;true&lt;/code&gt; as 1 (like in C)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;literally it means, booleans inside non-boolean operands (such as arrays) are not replaced.&lt;/li&gt;
&lt;li&gt;I am serious here, &lt;code&gt;false != "false"&lt;/code&gt; and &lt;code&gt;false == "0"&lt;/code&gt; !!&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;if one of the operands &lt;em&gt;is a number (or boolean), it coerces the other operand to a number&lt;/em&gt; and checks equality again&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;probably indirectly by coercing it's string&lt;/li&gt;
&lt;li&gt;note the subsection about NaNs below, any non-coercible thing becomes a NaN (except for Symbols)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;empty strings are 0! Therefore, empty arrays as well.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;values without &lt;code&gt;.toString()&lt;/code&gt; either become NaN or throw a type error&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;else if one of both is a string, it coerces the other operand to a string and compares both&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the standard behaviour boils down to &lt;code&gt;.toString()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;arrays are turned into a comma-separated list of element strings (without surrounding brackets!)

&lt;ul&gt;
&lt;li&gt;unfortunately, these representations are &lt;strong&gt;different from the string cast&lt;/strong&gt; &lt;code&gt;String()&lt;/code&gt; and different from the coercion of &lt;code&gt;==&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;it can be useful for deep-equality of number strings where shape of multi-dimensional arrays does not matter&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;else, if coercion to a "primitive value" (such as number or string) was not applicable, it compares whether both operands are the same one object. (Functions, arrays and objects won't be equal if not the same.)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;functions of different objects of equal class will be equal; they come from the same prototype&lt;/li&gt;
&lt;li&gt;The equality check will not work with Date objects! Instead, see the takeaways below.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;one special type of value, the &lt;code&gt;Symbol&lt;/code&gt;, can only be equal by reference (not to numbers or strings)&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;A careful reader might have noticed that some values cannot be coerced, they don't have a &lt;code&gt;valueOf()&lt;/code&gt; or &lt;code&gt;toString()&lt;/code&gt;. And standard objects without &lt;code&gt;toString()&lt;/code&gt; will throw a type error. But &lt;code&gt;null&lt;/code&gt; and &lt;code&gt;undefined&lt;/code&gt; are treated as mutually equal and unequal to everything else.&lt;/p&gt;

&lt;p&gt;All of this has interesting implications, such as:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;[0] == false&lt;/code&gt;, &lt;code&gt;"0" == false&lt;/code&gt;, &lt;code&gt;0 == false&lt;/code&gt;, &lt;code&gt;[] == false&lt;/code&gt;, &lt;code&gt;[false] == "false"&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;[0] == 0&lt;/code&gt;, &lt;code&gt;"" == 0&lt;/code&gt;, &lt;code&gt;".00" == 0&lt;/code&gt;, &lt;code&gt;[] == 0&lt;/code&gt;, &lt;code&gt;["   000.000  "] == 0&lt;/code&gt;, &lt;code&gt;[[[[[0]]]]] == "0"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;but&lt;/p&gt;

&lt;p&gt;&lt;code&gt;[false] != 0&lt;/code&gt;, &lt;code&gt;[false] != false&lt;/code&gt;, &lt;code&gt;"false" != false&lt;/code&gt;, &lt;code&gt;[[[[[0]]]]] != "  .0 "&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Comparing NaN
&lt;/h3&gt;

&lt;p&gt;This is a special case.&lt;/p&gt;

&lt;p&gt;No matter what you do, if you ever compare &lt;code&gt;==&lt;/code&gt; of a NaN, it will produce a &lt;code&gt;false&lt;/code&gt;. &lt;code&gt;!=&lt;/code&gt; will always be &lt;code&gt;true&lt;/code&gt;. It goes completely against the other rules.&lt;/p&gt;

&lt;p&gt;Right, &lt;code&gt;NaN === NaN&lt;/code&gt; is completely &lt;code&gt;false&lt;/code&gt;. But! &lt;code&gt;Object.is(NaN, NaN)&lt;/code&gt; ignores this special rule and returns &lt;code&gt;true&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
It seems, JavaScript treats &lt;code&gt;a != b&lt;/code&gt; as &lt;code&gt;!(a == b)&lt;/code&gt; so that &lt;code&gt;NaN != NaN&lt;/code&gt; returns &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It seems contrived that we have to use &lt;code&gt;isNaN()&lt;/code&gt; for checking NaNs.&lt;br&gt;
It is rather exceptional that of all things JavaScript follows the IEEE752 standard here, whereas it otherwise violates the mathematical standard semantics of equivalence relations.&lt;/p&gt;

&lt;p&gt;Despite that, it is useful to have an absorbing element for equality. NaN originally is an error value in IEEE752. The IEEE task force probably thought, any subsequent computations based on it wouldn't make sense, therefore would result in NaN as well. Starting computations based on &lt;code&gt;NaN&lt;/code&gt; wouldn't make sense then and could be skipped.&lt;/p&gt;

&lt;h3&gt;
  
  
  Relational comparisons
&lt;/h3&gt;

&lt;p&gt;While everybody complains about &lt;code&gt;==&lt;/code&gt;, rarily people seem to notice or point out, that there is no strict version for the other relational comparisons &lt;code&gt;&amp;lt;&lt;/code&gt;, &lt;code&gt;&amp;lt;=&lt;/code&gt;, &lt;code&gt;&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;gt;=&lt;/code&gt;. Typescript checks them as if they were strict.&lt;/p&gt;

&lt;p&gt;Comparing Dates, strings (lexicographically) and numbers is pretty straight forward but what happens with a mix of types? It is similar to loose equality but will not compare object references.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Did you know that &lt;code&gt;"  1 " &amp;lt; "1"&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt; but &lt;code&gt;"  1 " &amp;lt; 1&lt;/code&gt; is &lt;code&gt;false&lt;/code&gt;? The first one is compared lexicographically, the second one with number coercion.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;NaN&lt;/code&gt;s always compare as false (except when using &lt;code&gt;!=&lt;/code&gt;).&lt;br&gt;
&lt;code&gt;null&lt;/code&gt; becomes a 0, &lt;code&gt;undefined&lt;/code&gt; becomes a &lt;code&gt;NaN&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Indeed, &lt;code&gt;({} &amp;lt;= {})&lt;/code&gt; and &lt;code&gt;[] &amp;lt;= {}&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt; for some reason but neither &lt;code&gt;({} &amp;lt; {})&lt;/code&gt; nor &lt;code&gt;({} == {})&lt;/code&gt; is true. &lt;code&gt;[] &amp;gt;= {}&lt;/code&gt; is &lt;code&gt;false&lt;/code&gt; as well.&lt;/p&gt;

&lt;p&gt;The comparison compares number coercions, otherwise string coercions and it won't compare by reference. &lt;code&gt;NaN&lt;/code&gt; and &lt;code&gt;undefined&lt;/code&gt; are the special case which always gives &lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;null&lt;/code&gt; is a special case among the special cases. It is treated like a 0 in relational comparisons. This means &lt;code&gt;0 &amp;lt;= null &amp;amp;&amp;amp; 0 &amp;gt;= null&lt;/code&gt; and even &lt;code&gt;null &amp;lt;= []&lt;/code&gt; and &lt;code&gt;null &amp;lt;= ""&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt; but &lt;code&gt;0 == null&lt;/code&gt; is &lt;code&gt;false&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;Symbols cannot be compared with relational operators, it will cause a type error.&lt;/p&gt;

&lt;h3&gt;
  
  
  Takeaways
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;a &amp;lt; b&lt;/code&gt; is &lt;strong&gt;not&lt;/strong&gt; equivalent to &lt;code&gt;!(a &amp;gt;= b)&lt;/code&gt; (you cannot rewrite negations when you have &lt;code&gt;undefined&lt;/code&gt; or &lt;code&gt;NaN&lt;/code&gt; values)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;a &amp;lt;= b&lt;/code&gt; is &lt;strong&gt;not&lt;/strong&gt; equivalent to &lt;code&gt;a &amp;lt; b || a == b&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;a == b&lt;/code&gt; is &lt;strong&gt;not&lt;/strong&gt; equivalent to &lt;code&gt;a &amp;lt;= b &amp;amp;&amp;amp; a &amp;gt;= b&lt;/code&gt; (equality/inequality and relational operators are different things)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;a &amp;lt;= b || a &amp;gt;= b&lt;/code&gt; is not always &lt;code&gt;true&lt;/code&gt;!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;undefined == undefined&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt; but &lt;code&gt;undefined &amp;lt;= undefined || undefined &amp;gt;= undefined&lt;/code&gt; is &lt;code&gt;false&lt;/code&gt;.&lt;br&gt;
   On the other hand, &lt;code&gt;[] &amp;lt;= [""] &amp;amp;&amp;amp; [] &amp;gt;= [""]&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt; but &lt;code&gt;[""] != []&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Booleans generally behave like 0 or 1. Like they do in loose equality.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;if you need equality, rather than an identity check, you can make use of &lt;code&gt;a &amp;lt;= b &amp;amp;&amp;amp; b &amp;gt;= a&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;works great for Dates&lt;/li&gt;
&lt;li&gt;works in specific cases for Objects with custom &lt;code&gt;toString()&lt;/code&gt; function&lt;/li&gt;
&lt;li&gt;arrays with only numbers, booleans, undefined and null elements work.&lt;/li&gt;
&lt;li&gt;use JSON.stringify(), it is safer but not entirely safe

&lt;ul&gt;
&lt;li&gt;functions, symbols cannot be turned into JSON and are like "erased"&lt;/li&gt;
&lt;li&gt;BigInts cause an error&lt;/li&gt;
&lt;li&gt;the order of object properties can be non-deterministic&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;ultimately, a 3rd party library such as &lt;code&gt;lodash&lt;/code&gt; is required for equality checking of objects&lt;/li&gt;

&lt;li&gt;does not work with arrays whose elements have commas &lt;code&gt;,&lt;/code&gt; in their string representation&lt;/li&gt;

&lt;li&gt;nested arrays are not visible in the relational comparison&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;&lt;p&gt;relational comparisons of Symbols will crash your script&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Outro
&lt;/h3&gt;

&lt;p&gt;Anything written here is meant for the good of others. If you know something better or something is awry, leave a constructive reminder in the comments.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>typescript</category>
    </item>
    <item>
      <title>The (un)inuitive meaning of "inline" in C</title>
      <dc:creator>Dorla Hutch</dc:creator>
      <pubDate>Sat, 03 May 2025 03:10:21 +0000</pubDate>
      <link>https://dev.to/dorla/the-uninuitive-meaning-of-inline-in-c-33kp</link>
      <guid>https://dev.to/dorla/the-uninuitive-meaning-of-inline-in-c-33kp</guid>
      <description>&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+-------------------+
|  + - - - - - +    |
| x I N L I N E y z |
|  + - - - - - +    |
+-------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Effectively, the &lt;code&gt;inline&lt;/code&gt; keyword in C turns a definition into a declaration, and serves to move the function body to the header file.&lt;/p&gt;

&lt;h4&gt;
  
  
  Actual meaning of the inline keyword
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;inline&lt;/code&gt; turns actual definitions pretty much into forward declarations. &lt;code&gt;inline&lt;/code&gt; definitions are shared in the header and the declaration (indicating the fallback implementation) goes into the implementation file. The non-inline declaration implicitly reuses any other definition in the same file and therefore turns into the implementation.&lt;/p&gt;

&lt;h4&gt;
  
  
  Conversion into inline functions
&lt;/h4&gt;

&lt;p&gt;Converting functions into &lt;code&gt;inline&lt;/code&gt; functions is easy: move the body from the implementation file into the header file and replace the &lt;code&gt;extern&lt;/code&gt; with the &lt;code&gt;inline&lt;/code&gt; keyword at the destination.&lt;br&gt;
(But consider the annoying refactoring overhead which only makes sense for significant performance gains. If your function is never inlined or &lt;code&gt;inline&lt;/code&gt; is undone later, it was in vain.)&lt;/p&gt;

&lt;p&gt;Before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// file.h&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;square&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// file.c&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;"file.h"&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;square&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// file.h&lt;/span&gt;
&lt;span class="kr"&gt;inline&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;square&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// file.c&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;"file.h"&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;span class="k"&gt;extern&lt;/span&gt; &lt;span class="kr"&gt;inline&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;square&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// "extern inline" is optional&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The non-inline declaration doesn't even require an &lt;code&gt;extern&lt;/code&gt; , because non-inline function declarations are &lt;code&gt;extern&lt;/code&gt; by default. &lt;code&gt;extern inline&lt;/code&gt; can also be used in the implemention file to emit a function from the definition.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I believe,&lt;/em&gt; there is no effective compilation difference between &lt;code&gt;extern inline&lt;/code&gt; and &lt;code&gt;extern&lt;/code&gt; except for expressing with &lt;code&gt;extern inline&lt;/code&gt; the intent that other coders should search the header file for the definition. (Not sure, if there is compiler-dependent semantics.) GCC seems to make no difference between them. But you need to turn optimization for GCC to inline functions.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;inline&lt;/code&gt; as assertion
&lt;/h4&gt;

&lt;p&gt;Without an extern declaration, the &lt;code&gt;inline&lt;/code&gt; asserts inlining of the function, i.e. compilation is made to fail if the compiler doesn't &lt;em&gt;freely&lt;/em&gt; choose to inline it. (E.g. it enforces compilation with optimization or fails ultimately when the function cannot be inlined.)&lt;/p&gt;

&lt;h4&gt;
  
  
  Original problem
&lt;/h4&gt;

&lt;p&gt;Interesting is, what is the sense behind this specification of the C committee? (Besides technical limitations such as C's translation units.)&lt;/p&gt;

&lt;p&gt;It seems, the original problem was &lt;strong&gt;not&lt;/strong&gt; to annotate functions as "inlinable". Functions are inlinable when the compiler chooses to inline them, independent of a keyword.&lt;br&gt;
But! It can only do so (in C) if it knows the definition. Since function declarations in the header lack a body, they cannot be inlined in foreign files. The only purpose of &lt;code&gt;inline&lt;/code&gt; is to use a function body that does not emit a named function, allowing for a function body in the header file.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;inline&lt;/code&gt; rather means, a body is inlined in the declaration, rather than at the call sites.&lt;/p&gt;

&lt;h4&gt;
  
  
  History
&lt;/h4&gt;

&lt;p&gt;Previously, the GCC compiler defined &lt;code&gt;extern inline&lt;/code&gt; what later became &lt;code&gt;inline&lt;/code&gt; in the standard. It made sense to think, that anything in headers should be &lt;code&gt;extern&lt;/code&gt;. The C committee apparently had a different notion of &lt;code&gt;extern&lt;/code&gt;, indicating that it is only type information and not the associated (function) definition (which could be the same or a different file).&lt;/p&gt;

&lt;p&gt;Extern declarations weren't even required before C23. Not providing them would just omit type checking for unknown functions (with warnings) and assume integer return type.&lt;/p&gt;

&lt;p&gt;Therefore, &lt;code&gt;extern&lt;/code&gt; did not express that the function is in a different file (this would be assumed automatically) but provides typing for the current file (that is sufficiently distinct from a definition). And if we have a definition (the &lt;code&gt;inline&lt;/code&gt; definition), we still need an actual signature (declaration) to automatically a emit a function in the current file.&lt;/p&gt;

&lt;h4&gt;
  
  
  Using &lt;code&gt;static inline&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Finally, there is also &lt;code&gt;static inline&lt;/code&gt;. If your compiler really treats all functions with known body (and suitable preconditions) as inlinable, then a &lt;code&gt;static inline&lt;/code&gt; function would be just a normal &lt;code&gt;static&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;After all, people likely use &lt;code&gt;static inline&lt;/code&gt; when they believe to increase the inlining probability by doing so. (This maybe could depend on the compiler.) Unless you know that, it might not be worth using &lt;code&gt;static inline&lt;/code&gt;. In return, there is a risk that every file which does not inline every call of it will create a copy of this function in that file. In big projects, this could lead to a lot of code duplication even with 90% inlining.&lt;/p&gt;

&lt;p&gt;A better probability for inlining is obtained for writing small pure functions. (Especially when passing compile-time known values to it.)&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://godbolt.org/#g:!((g:!((g:!((h:codeEditor,i:(filename:'1',fontScale:14,fontUsePx:'0',j:1,lang:___c,selection:(endColumn:38,endLineNumber:9,positionColumn:38,positionLineNumber:9,selectionStartColumn:38,selectionStartLineNumber:9,startColumn:38,startLineNumber:9),source:'static+int+foo(int+x,+int+*y)%0A%7B%0A++++if+(x+%3C+0)%0A++++++++return+*y%3B%0A++++%0A%09return+x+%2B+3%3B%0A%7D%0A%0Aextern+inline+int+foo(int+x,+int+*y)%3B%0A%0A%23include+%3Cstdio.h%3E%0A%0Aint+main()%0A%7B%0A++++int+y+%3D+10%3B%0A%0A%09printf(%22%25d%22,+foo(-1,+%26y))%3B%0A%7D'),l:'5',n:'0',o:'C+source+%231',t:'0')),k:50,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((h:compiler,i:(compiler:cg151,filters:(b:'0',binary:'1',binaryObject:'1',commentOnly:'0',debugCalls:'1',demangle:'0',directives:'0',execute:'1',intel:'0',libraryCode:'0',trim:'1',verboseDemangling:'0'),flagsViewOpen:'1',fontScale:14,fontUsePx:'0',j:1,lang:___c,libs:!(),options:'-O1',overrides:!(),selection:(endColumn:1,endLineNumber:1,positionColumn:1,positionLineNumber:1,selectionStartColumn:1,selectionStartLineNumber:1,startColumn:1,startLineNumber:1),source:1),l:'5',n:'0',o:'+x86-64+gcc+15.1+(Editor+%231)',t:'0')),k:50,l:'4',n:'0',o:'',s:0,t:'0')),l:'2',n:'0',o:'',t:'0')),version:4" rel="noopener noreferrer"&gt;most recent GCC&lt;/a&gt;, static functions can silently be removed from the source code (with optimization turned on). In contrast, this does NOT happen with extern (including extern inline) declarations which always emit the function even if unused.&lt;/p&gt;

&lt;p&gt;This means, if your &lt;code&gt;static inline&lt;/code&gt; header function is always inlined, then &lt;code&gt;static inline&lt;/code&gt; is as good as &lt;code&gt;inline&lt;/code&gt; or &lt;code&gt;static&lt;/code&gt; without an additional extern declaration. But if not, you get no linker error but code duplication in the linked binary, when using &lt;code&gt;static&lt;/code&gt; (or &lt;code&gt;static inline&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;code&gt;static inline&lt;/code&gt; indeed ignores &lt;code&gt;extern&lt;/code&gt; declarations which follow after it but will not tolerate when they precede it.&lt;/p&gt;

&lt;h4&gt;
  
  
  Enforcing function inlining
&lt;/h4&gt;

&lt;p&gt;And if you really need to enforce inlining, you need to use a compiler feature such as &lt;code&gt;__attribute__((always_inline))&lt;/code&gt; in GCC. It cannot inline every function (for example when a function pointer is used) and could fail to compile in such a case but it will try harder.&lt;/p&gt;




&lt;p&gt;Anything to add or correct? Let me know.&lt;/p&gt;

</description>
      <category>c</category>
      <category>programming</category>
    </item>
    <item>
      <title>Most people understand Referential Transparency wrong</title>
      <dc:creator>Dorla Hutch</dc:creator>
      <pubDate>Thu, 31 Oct 2024 00:26:13 +0000</pubDate>
      <link>https://dev.to/dorla/most-people-understand-referential-transparency-wrong-1k2e</link>
      <guid>https://dev.to/dorla/most-people-understand-referential-transparency-wrong-1k2e</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Froyg6lygvv408btqfuzx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Froyg6lygvv408btqfuzx.png" alt="Titel Image" width="800" height="336"&gt;&lt;/a&gt;&lt;br&gt;
(Image from &lt;a href="https://dev.to/stefanalfbo/referential-transparency-30ni"&gt;Stefan Alfbo's Post&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;What do you think of when "Referential Transparency" comes to your mind? Perhaps you are thinking about purely functional programming languages. But People who think of it only this way, do not have a complete picture of referential transparency.&lt;/p&gt;

&lt;p&gt;———&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conception&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I have been browsing Stack Overflow and found no satisfying explanation about it. For me, it's no surprise that I know of no language which allows for expressing real-time and system-programming behaviour in a referentially transparent way.&lt;/p&gt;

&lt;p&gt;Usually, referential transparency (in programming languages) is seen as a subset of expressions or functions whose result value can be replaced by a constant value without changing program semantics. Coders believe, it is necssary to constrain themselves to obtain referential transparency. They think of functions which do not have side effects. While this is just a special case of referential transparency, this is not a necessary constraint.&lt;/p&gt;

&lt;p&gt;Here is, how I understand Referential Transparency:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Referential transparency is a property of a type system. With referential transparency, the system offers types whose element values can be used to replace any specific language expression without causing a change in the language's &lt;em&gt;specified&lt;/em&gt; execution semantics.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;———&lt;/p&gt;

&lt;p&gt;What we need for referential transparency are values that can convey execution semantics (as much as they practically matter), including side effects.&lt;/p&gt;

&lt;p&gt;This means, we could have a referentially transparent OOP language if the type system would be complex enough and permits type checks. &lt;/p&gt;

&lt;p&gt;Code blocks or thunks would be values of such types. We could specify a parameter type to expect a value whose semantics does not alter shared variables. Or specify that a parameter value calls a function or writes IO a given number of times in a specific order (a side effect iterator of some kind).&lt;/p&gt;

&lt;p&gt;Truth is, purely functional programming languages have side channels and side effects too but they are deliberately omitted from the language's semantics specification. If not optimized away, function calls have time and memory cost. &lt;strong&gt;Leaky Abstraction&lt;/strong&gt; is another catchphrase for this issue which occurs when internal details are excluded from the public specification of abstractions. They produce effects that cannot be explained within the simplified specification of the language. Information leakage is due to incomplete explanation of the abstraction, not due to the abstraction itself.&lt;/p&gt;

&lt;p&gt;Referential transparency is not a property reserved to functional programming. The problem just is, other popular languages are not trying to enhance their type system in adding side effects to their type checking.&lt;/p&gt;

&lt;p&gt;In comparison, if we ask a competent human being, the semantics of programming code is sufficiently transparent (even when it needs compiler- and computer-specific knowledge). If we ask a competent developer to refactor code into a method call, they are able to do it (if anyhow possible). With improved types, programming language interpreters could refactor code for optimization purposes.&lt;/p&gt;

&lt;p&gt;Global variables, (real)time, space requirements or other system functionality (IO, storage access and system calls) are typically considered to be side effects (technically, memory access is also one due to modification of caches). But, they are mainly called "side effects" because that stuff is not modelled in the type system.&lt;/p&gt;

&lt;p&gt;———&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Referential Fallacies&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Referential transparency arises when semantics is a closed system. In natural language however, words generally have multiple or contextual meanings and may dynamically change over time, the expressed intension might even differ from the words' direct meaning, like in idioms or ironic statements.&lt;/p&gt;

&lt;p&gt;If two things are &lt;em&gt;equivalent&lt;/em&gt;, they can be exchanged and it will not alter the meaning. Referential opacity is the case where words are not equivalent even if they share a reference to the same entity (linguists say "co-referential").&lt;/p&gt;

&lt;p&gt;Example time. If a person has two names "Al" and "Bo" (of course I mean Alex and Boris, what were you thinking 😉?), these names are not equivalent. The expression "A name of Al" has even multiple meanings (it could be "Al" or "Bo" or both). If we use "the" as in "The name of Al", we are refering to something that mathematicians call a "principal value". It produces the same meaning as "The name of Bo". (I interpret it as "The [principal] name of Al".)&lt;/p&gt;

&lt;p&gt;The reason, why "Al" and "Bo" are not equivalent is, because words are more than one reference. They also refer to themselves as a name entity in a second meaning. (Disambiguating the meaning is subject to a context.) The sentence "Al begins with an 'A'" shows the second referential meaning of "Al": the reference to itself as a name object. In that regard, "Bo" is not equivalent because when "Bo" refers to a name object it's a different one with different letters, even if both names have the same referent (in a temporary context!).&lt;/p&gt;

&lt;p&gt;Let's take a case from a popular animation show: assume, "Ladybug" is a cover name of "Marinette" and there is a boy "Adrien" who does not know of "Ladybug"'s true identity. We know that "Adrien loves Ladybug". Is the proposition referentially opaque because "Adrien loves Marinette" does not hold? No. Objectively seen, it is still true, because "Marinette" and "Ladybug" may refer to the same person and Adrien just does not know it. Subjectively, if Adrien would be asked about it, he would probably say yes or no to the second statement, because he doesn't know the truth. (This is certainly the notable part early in the show, that a real perception of (no) feelings doesn't have to match its actual truth.)&lt;/p&gt;

&lt;p&gt;With another argument, Ladybug has her own skills and personality aside from Marinette. Both names do not have the same meaning and so the meaning of "loves Ladybug" and "loves Marinette" really is a different one. The previous argument would hold however if we had two names which are exactly aliases, not differing in context.&lt;/p&gt;

&lt;p&gt;———&lt;/p&gt;

&lt;p&gt;The previously mentioned Wikipedia article also claims &lt;code&gt;_ lives in _&lt;/code&gt; (e.g. &lt;code&gt;She lives in London.&lt;/code&gt;) were referentially transparent and &lt;code&gt;_ contains _&lt;/code&gt; (e.g. &lt;code&gt;'London' contains 6 letters.&lt;/code&gt;) were referentially opaque. Both does not hold in general, there is no referential transparency property for natural language predicates. It is the &lt;em&gt;verbatim context&lt;/em&gt; (characters appearing inside a string) which makes the quoted part of the sentence referentially opaque. Of course I can correctly say "The name of London contains six letters." (or the name of England's Capital) and I can say "She lives in 'London'" (but not "She lives in 'England's Capital'").&lt;/p&gt;

&lt;p&gt;Does this mean, code inside string quotes is referentially opaque? Not in general. Funny thing is, did you know that Mr. Quine also is the one who came up with the idea of string interpolation in logic? (He called it &lt;strong&gt;Quasi-Quotation&lt;/strong&gt;, using ⌜…⌝ as quotes.) Because in the context of string or code interpolation, we can make string contents referentially transparent.&lt;/p&gt;

&lt;p&gt;Check out this case of referential transparency: &lt;code&gt;She lives in ⌜{_}⌝&lt;/code&gt; or &lt;code&gt;⌜{_}⌝ contains 6 letters&lt;/code&gt;. (We can use &lt;code&gt;London&lt;/code&gt; and &lt;code&gt;Capital of England&lt;/code&gt; at the same time.)&lt;/p&gt;

&lt;p&gt;Typically, referential opacity occurs with words which are co-referential in the limited definition of a typesystem but not sufficiently close to the typesystem in the actual meaning.&lt;/p&gt;

&lt;p&gt;———&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prospect&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Why after all would it be a problem to think referential transparency as only side-effect-less functions, if it works?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Because side-effects matter in practical software, the ability to hide extraneous mechanical details with side effects can reduce reading and writing overhead. The rejection of side-effects can limit expressivity. One consequence of the view of "referential transparency → no side effects" is, strictly referentially transparent languages (that I know of) have no realtime semantics (Haskell only has one for debugging purposes).&lt;/p&gt;

&lt;p&gt;Nothing prevents us from defining types with time, space or other side effect semantics. A value with a duration of 3 seconds could be interpreted as a busy waiting loop with a sufficient number of instructions when it takes effect. A value can have a sequence of certain effects as properties, such as IO, memory operations or memory constraints. This means, we could specify allocation regions as part of a type. These "effect values" only need to be first-class citizens and can also be combined with other values.&lt;/p&gt;

&lt;p&gt;An impediment could be, it makes type checking more complicated. A future-relevant language should make an ideal specification and treat realization as a research challenge. Maybe it isn't decidable in 100% of the theoretical cases but why not still permitting types for the cases that practically are decidable by a resource constraint? It does not sound plausible to me that someone actually needs code in practice which must have undecidable semantic properties. Practical software usage is bounded by limited resources. Code which exceeds specified resource limits of time and memory is unacceptable.&lt;/p&gt;

&lt;p&gt;This is the end of my monologue for now. You think I confused something? Let us know in the comments.&lt;/p&gt;

</description>
      <category>typetheory</category>
      <category>programming</category>
      <category>languagedesign</category>
    </item>
  </channel>
</rss>
