<turbo-stream action="append" target="posts_list"><template>    <div class="postbit" id="159974" data-post-id="159974">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="thousandsofthem" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/thousandsofthem/120/1030_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  thousandsofthem
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p><a class="mention" href="/u/overminddl1" rel="nofollow">@OvermindDL1</a><br>
Any code to back your measurements?</p>
<p>Also, comparing decimals is as bad as comparing dates <img src="https://forum.elixirforum.com/images/emoji/apple/frowning.png?v=15" title=":frowning:" class="emoji" alt=":frowning:" loading="lazy" width="20" height="20"><br>
Don’t think structs vs records can do anything here, nor elixir/erlang can help with this.<br>
P.S. i wonder if it’s possible to make <strong>standard</strong> macro to take advantage of that standard <code>compare/2</code> thing - e.g.</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">if foocompare(decimal1 &gt;= decimal2) do
...
</code></pre> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="159974" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/decimal-1-9-and-2-0/28438/12">Post #11</a>
	                </div>
	            </div>
              <div id="likers-container-159974" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="159974"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #11"></div>
  </section>
</div>
    <div class="postbit" id="159982" data-post-id="159982">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="Qqwy" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/Qqwy/120/1349_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  Qqwy
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>TypeCheck Core Team</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote group-livebook_core_team" data-username="josevalim" data-post="5" data-topic="28438">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/josevalim/48/1787_2.png" class="avatar"> josevalim:</div>
<blockquote>
<p>Checking all tuples for potentially being a record was too expensive and it had too many false positives, meaning we would call the implementation code for something that would not be a record and then it would fail.</p>
</blockquote>
</aside>
<p>Was this before protocol consolidation?<br>
I believe <a class="mention" href="/u/overminddl1" rel="nofollow">@OvermindDL1</a> is referring to <a href="https://github.com/OvermindDL1/protocol_ex/blob/master/test/bench/numbers_test.exs" rel="noopener nofollow ugc">some testing/benchmarking code</a> in the <a href="https://hex.pm/packages/protocol_ex" rel="nofollow">ProtocolEx</a> library.</p>
<hr>
<p><a class="mention" href="/u/wojtekmach" rel="nofollow">@wojtekmach</a>  Congradulations! This seems like a good step forward <img src="https://forum.elixirforum.com/images/emoji/apple/slight_smile.png?v=15" title=":slight_smile:" class="emoji" alt=":slight_smile:" loading="lazy" width="20" height="20">.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="159982" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/decimal-1-9-and-2-0/28438/13">Post #12</a>
	                </div>
	            </div>
              <div id="likers-container-159982" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="159982"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #12"></div>
  </section>
</div>
    <div class="postbit" id="160009" data-post-id="160009">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="josevalim" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/josevalim/120/1787_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  josevalim
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Creator of Elixir</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="Qqwy" data-post="13" data-topic="28438">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/qqwy/48/1349_2.png" class="avatar"> Qqwy:</div>
<blockquote>
<p>Was this before protocol consolidation?</p>
</blockquote>
</aside>
<p>Before consolidation. But consolidation won’t fix false positives.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="160009" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/decimal-1-9-and-2-0/28438/14">Post #13</a>
	                </div>
	            </div>
              <div id="likers-container-160009" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="160009"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #13"></div>
  </section>
</div>
    <div class="postbit" id="160010" data-post-id="160010">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="OvermindDL1" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/OvermindDL1/120/2677_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  OvermindDL1
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote group-Hex-Core-Team" data-username="wojtekmach" data-post="3" data-topic="28438" data-full="true">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/wojtekmach/48/999_2.png" class="avatar"> wojtekmach:</div>
<blockquote>
<p>We haven’t discussed this. That might be too big of a leap if that makes sense, for example we’d no longer have protocols.</p>
</blockquote>
</aside>
<p>Extending protocols to support records is as trivial as testing the first element of the tuple if it’s module implements the protocol.  <img src="https://forum.elixirforum.com/images/emoji/apple/slight_smile.png?v=15" title=":slight_smile:" class="emoji" alt=":slight_smile:" loading="lazy" width="20" height="20"></p>
<p>It’s a feature I’ve wanted and talked about for many years.  ^.^</p>
<aside class="quote group-Hex-Core-Team" data-username="wojtekmach" data-post="3" data-topic="28438">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/wojtekmach/48/999_2.png" class="avatar"> wojtekmach:</div>
<blockquote>
<p>But your results are definitely interesting, can you share library and benchmark code?</p>
</blockquote>
</aside>
<p>It was for a benchmark comparing Coerce’s and Number’s protocols reimplemented in ProtocolEx, implemented for integers, float, Decimal, and MyDecimal (copied from Decimal but using records instead of structs, I was curious how it would do, just enough of it was implemented for the test).  The code is available in ProtocolEx’s test directory but the results run right now on my local computer are:</p>
<pre data-code-wrap="zsh"><code class="lang-zsh">➜ mix test --include bench:true
Including tags: [bench: "true"]

....Operating System: Linux
CPU Information: AMD Phenom(tm) II X6 1090T Processor
Number of Available Cores: 6
Available memory: 15.67 GB
Elixir 1.9.1
Erlang 22.2.1

Benchmark suite executing with the following configuration:
warmup: 3 s
time: 3 s
memory time: 0 ns
parallel: 1
inputs: Decimal, Floats, Integers, MyDecimal
Estimated total run time: 2 min


Benchmarking MyNumbers with input Decimal...
Benchmarking MyNumbers with input Floats...
Benchmarking MyNumbers with input Integers...
Benchmarking MyNumbers with input MyDecimal...
Benchmarking MyNumbers - sans coerce with input Decimal...
Benchmarking MyNumbers - sans coerce with input Floats...
Benchmarking MyNumbers - sans coerce with input Integers...
Benchmarking MyNumbers - sans coerce with input MyDecimal...
Benchmarking Numbers with input Decimal...
Benchmarking Numbers with input Floats...
Benchmarking Numbers with input Integers...
Benchmarking Numbers with input MyDecimal...
Benchmarking Numbers - my coerce with input Decimal...
Benchmarking Numbers - my coerce with input Floats...
Benchmarking Numbers - my coerce with input Integers...
Benchmarking Numbers - my coerce with input MyDecimal...
Benchmarking Numbers - sans coerce with input Decimal...
Benchmarking Numbers - sans coerce with input Floats...
Benchmarking Numbers - sans coerce with input Integers...
Benchmarking Numbers - sans coerce with input MyDecimal...

##### With input Integers #####
Name                              ips        average  deviation         median         99th %
MyNumbers - sans coerce        9.08 M      110.15 ns ±16673.74%          73 ns         279 ns
MyNumbers                      4.68 M      213.63 ns  ±7430.41%         151 ns         465 ns
Numbers - sans coerce          3.94 M      253.98 ns  ±9300.43%         180 ns         474 ns
Numbers                        2.87 M      348.04 ns  ±5574.50%         247 ns         656 ns
Numbers - my coerce            2.77 M      361.31 ns  ±6628.20%         243 ns         661 ns

Comparison: 
MyNumbers - sans coerce        9.08 M
MyNumbers                      4.68 M - 1.94x slower
Numbers - sans coerce          3.94 M - 2.31x slower
Numbers                        2.87 M - 3.16x slower
Numbers - my coerce            2.77 M - 3.28x slower

##### With input Floats #####
Name                              ips        average  deviation         median         99th %
MyNumbers - sans coerce        8.95 M      111.72 ns  ±1351.18%          83 ns         313 ns
MyNumbers                      4.60 M      217.38 ns   ±759.57%         181 ns         518 ns
Numbers - sans coerce          2.12 M      471.92 ns ±11747.10%         217 ns         586 ns
Numbers - my coerce            1.72 M      582.45 ns  ±9204.68%         296 ns         761 ns
Numbers                        1.71 M      586.38 ns  ±9112.67%         287 ns         794 ns

Comparison: 
MyNumbers - sans coerce        8.95 M
MyNumbers                      4.60 M - 1.95x slower
Numbers - sans coerce          2.12 M - 4.22x slower
Numbers - my coerce            1.72 M - 5.21x slower
Numbers                        1.71 M - 5.25x slower

##### With input MyDecimal #####
Name                              ips        average  deviation         median         99th %
MyNumbers - sans coerce        1.53 M      653.91 ns  ±3127.51%         491 ns        1230 ns
MyNumbers                      1.30 M      768.43 ns  ±5945.41%         553 ns        1386 ns
Numbers - sans coerce          1.22 M      821.94 ns  ±2839.92%         663 ns        2528 ns
Numbers                        1.13 M      884.97 ns  ±4559.10%         676 ns        1660 ns
Numbers - my coerce            1.09 M      914.85 ns  ±4804.80%         678 ns        2588 ns

Comparison: 
MyNumbers - sans coerce        1.53 M
MyNumbers                      1.30 M - 1.18x slower
Numbers - sans coerce          1.22 M - 1.26x slower
Numbers                        1.13 M - 1.35x slower
Numbers - my coerce            1.09 M - 1.40x slower

##### With input Decimal #####
Name                              ips        average  deviation         median         99th %
MyNumbers - sans coerce      330.74 K        3.02 μs   ±912.21%        2.56 μs        6.25 μs
MyNumbers                    327.97 K        3.05 μs  ±1079.04%        2.68 μs        6.06 μs
Numbers - my coerce          316.77 K        3.16 μs  ±1057.15%        2.78 μs        6.36 μs
Numbers                      314.40 K        3.18 μs   ±785.11%        2.81 μs        6.38 μs
Numbers - sans coerce        305.82 K        3.27 μs   ±999.41%        2.70 μs        6.86 μs

Comparison: 
MyNumbers - sans coerce      330.74 K
MyNumbers                    327.97 K - 1.01x slower
Numbers - my coerce          316.77 K - 1.04x slower
Numbers                      314.40 K - 1.05x slower
Numbers - sans coerce        305.82 K - 1.08x slower
.

Finished in 157.5 seconds
1 property, 4 tests, 0 failures

Randomized with seed 501954
</code></pre>
<p>I entirely admit I may have screwed something up in the implementation (see github, clone ProtocolEx and run <code>mix test --include bench:true</code>), but the MyDecimal speed over Decimal was uniformly much higher in both Protocol’s Numbers/Coerce’s and ProtocolEx’s implementations.  The actual code for the MyDecimal module is in <code>lib/protocol_ex.ex</code> (only when the environment is <code>test</code>, I really need to move it out, it was just some testing code before I got around to pulling out all the tests that I never ended up moving out, but it’s at the bottom of the file).  The difference was I implemented it in the protocol itself so it didn’t have to call another class but saving a single module call doesn’t seem a 5x difference.</p>
<aside class="quote group-livebook_core_team" data-username="josevalim" data-post="5" data-topic="28438">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/josevalim/48/1787_2.png" class="avatar"> josevalim:</div>
<blockquote>
<p>Before Elixir v1.0, we had protocols for Records and there were many issues. For example, is <code>{:name, "hello"}</code> a record or not? Checking all tuples for potentially being a record was too expensive and it had too many false positives, meaning we would call the implementation code for something that would not be a record and then it would fail. That’s one of the reasons why we introduced structs in the first place.</p>
</blockquote>
</aside>
<p>Not unless the consolidation built the record tests like the struct tests where it tests explicit names first before the tuple itself.</p>
<aside class="quote group-livebook_core_team" data-username="josevalim" data-post="5" data-topic="28438">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/josevalim/48/1787_2.png" class="avatar"> josevalim:</div>
<blockquote>
<p>I am honestly skeptical this would be case. Records are not 5x faster than structs even on regular operationss, so once it gets diluted with the operations done by Decimal, I really don’t expect a 5 times improvement. <img src="https://forum.elixirforum.com/images/emoji/apple/slight_smile.png?v=15" title=":slight_smile:" class="emoji" alt=":slight_smile:" loading="lazy" width="20" height="20"></p>
</blockquote>
</aside>
<aside class="quote no-group quote-modified" data-username="Sanjibukai" data-post="8" data-topic="28438">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/s/ecd19e/48.png" class="avatar"> Sanjibukai:</div>
<blockquote>
<p>In the meantime I just discovered (while searching on the forum about how people are dealing with currencies) <a href="https://hexdocs.pm/ex_money/readme.html" rel="noopener nofollow ugc">ex_money</a> which seems to depend indeed on the <code>decimal</code> library… So I bet that <code>ex_money</code> doesn’t simply consider values as integer in cents…</p>
</blockquote>
</aside>
<p>Yes!  Never use floats for currency ever ever ever!  Decimal and a wrapper like ex_money is what should be used!</p>
<aside class="quote no-group" data-username="tmbb" data-post="9" data-topic="28438">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/t/77aa72/48.png" class="avatar"> tmbb:</div>
<blockquote>
<p>That might be why <a class="mention" href="/u/overminddl1" rel="nofollow">@OvermindDL1</a> sees such dramatic improvements</p>
</blockquote>
</aside>
<p>I just needed to fulfill the multiplication and addition API’s, so those functions (and the helpers Decimal used like <code>pow10</code> is all I copied in.  It could just have been that this code is extra susceptible to such changes.</p>
<aside class="quote group-livebook_core_team" data-username="josevalim" data-post="11" data-topic="28438">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/josevalim/48/1787_2.png" class="avatar"> josevalim:</div>
<blockquote>
<p>My point is that records are not 5 times faster than structs even when you are only performing records and structs operations, so when you increase the baseline by adding decimal operations as well, it is even more unlikely to get such a number.</p>
</blockquote>
</aside>
<p>Yeah that’s my thoughts, let’s write a bench to compare just base structs and records…  Ooo, I already have one in my playground I just noticed, let’s run it.</p>
<p>Getting data out:</p>
<pre data-code-wrap="zsh"><code class="lang-zsh">Benchmarking Classifier: get
============================

Operating System: Linux"
CPU Information: AMD Phenom(tm) II X6 1090T Processor
Number of Available Cores: 6
Available memory: 15.67 GB
Elixir 1.9.1
Erlang 22.2.1

Benchmark suite executing with the following configuration:
warmup: 2 s
time: 2 s
memory time: 2 s
parallel: 1
inputs: none specified
Estimated total run time: 1.20 min


Benchmarking Record1-direct...
Benchmarking Record1-remote...
Benchmarking Record1-stock...
Benchmarking Record9-first-direct...
Benchmarking Record9-first-remote...
Benchmarking Record9-first-stock...
Benchmarking Record9-last-direct...
Benchmarking Record9-last-remote...
Benchmarking Record9-last-stock...
Benchmarking Struct1...
Benchmarking Struct9-first...
Benchmarking Struct9-last...

Name                           ips        average  deviation         median         99th %
Record9-last-direct        26.21 M      0.0382 μs     ±6.99%      0.0370 μs      0.0460 μs
Record1-direct             25.82 M      0.0387 μs   ±622.14%      0.0400 μs      0.0900 μs
Record9-first-direct       25.74 M      0.0388 μs     ±7.88%      0.0370 μs      0.0480 μs
Record9-last-stock         25.64 M      0.0390 μs     ±7.90%      0.0370 μs      0.0480 μs
Record9-first-stock        25.56 M      0.0391 μs     ±8.28%      0.0380 μs      0.0500 μs
Record1-stock              25.45 M      0.0393 μs     ±8.32%      0.0370 μs      0.0490 μs
Struct1                    22.00 M      0.0455 μs   ±647.96%      0.0400 μs       0.100 μs
Struct9-first              21.04 M      0.0475 μs   ±622.37%      0.0400 μs       0.110 μs
Struct9-last               19.80 M      0.0505 μs    ±11.05%      0.0480 μs      0.0650 μs
Record1-remote             11.50 M      0.0870 μs     ±9.04%      0.0820 μs       0.116 μs
Record9-last-remote        11.50 M      0.0870 μs     ±5.79%      0.0840 μs       0.100 μs
Record9-first-remote       11.26 M      0.0888 μs     ±7.10%      0.0860 μs       0.104 μs

Comparison: 
Record9-last-direct        26.21 M
Record1-direct             25.82 M - 1.01x slower
Record9-first-direct       25.74 M - 1.02x slower
Record9-last-stock         25.64 M - 1.02x slower
Record9-first-stock        25.56 M - 1.03x slower
Record1-stock              25.45 M - 1.03x slower
Struct1                    22.00 M - 1.19x slower
Struct9-first              21.04 M - 1.25x slower
Struct9-last               19.80 M - 1.32x slower
Record1-remote             11.50 M - 2.28x slower
Record9-last-remote        11.50 M - 2.28x slower
Record9-first-remote       11.26 M - 2.33x slower

Memory usage statistics:

Name                    Memory usage
Record9-last-direct             24 B
Record1-direct                  24 B - 1.00x memory usage
Record9-first-direct            24 B - 1.00x memory usage
Record9-last-stock              24 B - 1.00x memory usage
Record9-first-stock             24 B - 1.00x memory usage
Record1-stock                   24 B - 1.00x memory usage
Struct1                         24 B - 1.00x memory usage
Struct9-first                   24 B - 1.00x memory usage
Struct9-last                    24 B - 1.00x memory usage
Record1-remote                  24 B - 1.00x memory usage
Record9-last-remote             24 B - 1.00x memory usage
Record9-first-remote            24 B - 1.00x memory usage

**All measurements for memory usage were the same**
</code></pre>
<p>Putting data in:</p>
<pre data-code-wrap="zsh"><code class="lang-zsh">Benchmarking Classifier: put
============================

Operating System: Linux"
CPU Information: AMD Phenom(tm) II X6 1090T Processor
Number of Available Cores: 6
Available memory: 15.67 GB
Elixir 1.9.1
Erlang 22.2.1

Benchmark suite executing with the following configuration:
warmup: 2 s
time: 2 s
memory time: 2 s
parallel: 1
inputs: none specified
Estimated total run time: 1.50 min


Benchmarking Record1-direct...
Benchmarking Record1-remote...
Benchmarking Record1-stock...
Benchmarking Record9-first-direct...
Benchmarking Record9-first-remote...
Benchmarking Record9-first-stock...
Benchmarking Record9-last-direct...
Benchmarking Record9-last-remote...
Benchmarking Record9-last-stock...
Benchmarking Struct1...
Benchmarking Struct1-opt...
Benchmarking Struct9-first...
Benchmarking Struct9-first-opt...
Benchmarking Struct9-last...
Benchmarking Struct9-last-opt...

Name                           ips        average  deviation         median         99th %
Record1-direct             18.88 M      0.0530 μs   ±729.48%      0.0500 μs       0.110 μs
Record1-stock              17.61 M      0.0568 μs   ±795.50%      0.0500 μs       0.110 μs
Struct1                    16.85 M      0.0594 μs   ±540.43%      0.0500 μs       0.120 μs
Record9-last-direct        15.99 M      0.0625 μs   ±724.38%      0.0500 μs       0.140 μs
Record9-first-direct       15.93 M      0.0628 μs   ±573.43%      0.0500 μs       0.130 μs
Record9-first-stock        15.90 M      0.0629 μs   ±721.68%      0.0500 μs       0.130 μs
Record9-last-stock         15.30 M      0.0653 μs   ±704.62%      0.0600 μs        0.24 μs
Struct1-opt                14.29 M      0.0700 μs   ±386.88%      0.0600 μs       0.150 μs
Struct9-first              13.78 M      0.0726 μs   ±448.21%      0.0600 μs       0.190 μs
Struct9-first-opt          12.62 M      0.0792 μs   ±300.47%      0.0700 μs       0.160 μs
Struct9-last               12.49 M      0.0801 μs  ±7517.80%           0 μs        0.40 μs
Struct9-last-opt           11.65 M      0.0858 μs   ±156.14%      0.0800 μs       0.150 μs
Record1-remote              9.30 M       0.108 μs   ±228.35%       0.100 μs       0.180 μs
Record9-last-remote         8.94 M       0.112 μs   ±182.77%       0.100 μs       0.180 μs
Record9-first-remote        8.42 M       0.119 μs   ±286.36%       0.110 μs        0.23 μs

Comparison: 
Record1-direct             18.88 M
Record1-stock              17.61 M - 1.07x slower
Struct1                    16.85 M - 1.12x slower
Record9-last-direct        15.99 M - 1.18x slower
Record9-first-direct       15.93 M - 1.19x slower
Record9-first-stock        15.90 M - 1.19x slower
Record9-last-stock         15.30 M - 1.23x slower
Struct1-opt                14.29 M - 1.32x slower
Struct9-first              13.78 M - 1.37x slower
Struct9-first-opt          12.62 M - 1.50x slower
Struct9-last               12.49 M - 1.51x slower
Struct9-last-opt           11.65 M - 1.62x slower
Record1-remote              9.30 M - 2.03x slower
Record9-last-remote         8.94 M - 2.11x slower
Record9-first-remote        8.42 M - 2.24x slower

Memory usage statistics:

Name                    Memory usage
Record1-direct                  48 B
Record1-stock                   48 B - 1.00x memory usage
Struct1                         64 B - 1.33x memory usage
Record9-last-direct            112 B - 2.33x memory usage
Record9-first-direct           112 B - 2.33x memory usage
Record9-first-stock            112 B - 2.33x memory usage
Record9-last-stock             112 B - 2.33x memory usage
Struct1-opt                     64 B - 1.33x memory usage
Struct9-first                  128 B - 2.67x memory usage
Struct9-first-opt              128 B - 2.67x memory usage
Struct9-last                   128 B - 2.67x memory usage
Struct9-last-opt               128 B - 2.67x memory usage
Record1-remote                  48 B - 1.00x memory usage
Record9-last-remote            112 B - 2.33x memory usage
Record9-first-remote           112 B - 2.33x memory usage

**All measurements for memory usage were the same**
</code></pre>
<p>So yeah likely something else coming into play, maybe that one less module call is really significant combined with the above…  Or I screwed up when I converted it.  Would love someone to check where I screwed up at.  ^.^;</p>
<p>Links are in <a class="mention" href="/u/qqwy" rel="nofollow">@Qqwy</a>’s post.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="160010" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/decimal-1-9-and-2-0/28438/15">Post #14</a>
	                </div>
	            </div>
              <div id="likers-container-160010" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="160010"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #14"></div>
  </section>
</div>
    <div class="postbit" id="160014" data-post-id="160014">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="josevalim" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/josevalim/120/1787_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  josevalim
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Creator of Elixir</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="OvermindDL1" data-post="15" data-topic="28438">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/overminddl1/48/2677_2.png" class="avatar"> OvermindDL1:</div>
<blockquote>
<p>Not unless the consolidation built the record tests like the struct tests where it tests explicit names first before the tuple itself.</p>
</blockquote>
</aside>
<p>This is only “trivial” if you dismiss a bunch of possible edge cases. Today two separate modules can define a record name <code>{:user, _}</code> with the same amount of fields. So which one wins over the protocol implementation and consolidation?</p>
<p>What you say would only be possible if we:</p>
<ol>
<li>tie records to modules</li>
<li>each module can only a single “tied record”</li>
</ol>
<p>Which is how structs behave and out of the gate rules out all of Erlang records. Unless we use another implementation of protocols, which are not open for extension as Elixir’s. So at the end of the day, you won’t have records as they are today OR you won’t have protocols as they are today.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="160014" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/decimal-1-9-and-2-0/28438/16">Post #15</a>
	                </div>
	            </div>
              <div id="likers-container-160014" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="160014"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #15"></div>
  </section>
</div>
    <div class="postbit" id="160015" data-post-id="160015">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="OvermindDL1" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/OvermindDL1/120/2677_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  OvermindDL1
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote group-livebook_core_team" data-username="josevalim" data-post="16" data-topic="28438">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/josevalim/48/1787_2.png" class="avatar"> josevalim:</div>
<blockquote>
<p>This is only “trivial” if you dismiss a bunch of possible edge cases. Today two separate modules can define a record name <code>{:user, _}</code> with the same amount of fields. So which one wins over the protocol implementation and consolidation?</p>
</blockquote>
</aside>
<p>Honestly such a setup should error in my opinion, as it would in a static language of similar consolidation.  There should only be a single implementation for, say, a given struct or record, implementing it multiple times always indicates a possible fault and should be errored at the earliest opportunity (don’t struct’s already error if implemented multiple times?).</p>
<aside class="quote group-livebook_core_team" data-username="josevalim" data-post="16" data-topic="28438">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/josevalim/48/1787_2.png" class="avatar"> josevalim:</div>
<blockquote>
<ul>
<li>tie records to modules</li>
<li>each module can only a single “tied record”</li>
</ul>
</blockquote>
</aside>
<p>I’d say that is what should be done, and yes that rules out erlang records <em>unless</em> someone <code>defmodule :some_erlang_record do ... end</code> of course, which is quite feasible as there can only be one module in the system (otherwise wrap it up into another record, a single indirection there is still quite cheap).</p>
<p>EDIT:  Just for some somewhat unrelated history, back in my Erlang days I tended to use a very generic name for non-public records like <code>:state</code> or so, or I used the name of the module itself for public records (or ‘namespaced’ on the module name for many records in a module).  I don’t think that’s an uncommon pattern, but still easy workarounds by either <code>defmodule</code> the name or wrap it.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="160015" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/decimal-1-9-and-2-0/28438/17">Post #16</a>
	                </div>
	            </div>
              <div id="likers-container-160015" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="160015"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #16"></div>
  </section>
</div>
    <div class="postbit" id="160019" data-post-id="160019">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="josevalim" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/josevalim/120/1787_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  josevalim
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Creator of Elixir</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="OvermindDL1" data-post="17" data-topic="28438">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/overminddl1/48/2677_2.png" class="avatar"> OvermindDL1:</div>
<blockquote>
<p>There should only be a single implementation for, say, a given struct or record, implementing it multiple times always indicates a possible fault and should be errored at the earliest opportunity (don’t struct’s already error if implemented multiple times?).</p>
</blockquote>
</aside>
<p>Exactly. If you want protocol dispatch for records, then you need to add these restrictions. But this is not how records have been used and adding these restrictions would leave many records unsupported. Especially considering that we inherited many of them from Erlang.</p>
<p>Defining modules around records wouldn’t always work either. For example, Erlang has records that are named after the modules that define them. So you obviously can’t redefine those modules unless we create some sort of special convention for wrapper modules, which ends up adding even more complexity and slowing down dispatch.</p>
<p>And again, this still does not solve the problem with false positives. It is absolutely ok for me to define a tuple <code>{:user, :ok}</code> that accidentally matches a record. The reason why structs solve this is exactly because we use the <code>__struct__</code> key, which is unlikely to conflict.</p>
<p>To be clear, I am not saying it is not possible, I am just saying that the claim it can be “trivially addressed” is unfair because the trivial solution comes with many requirements and pitfalls. And if the requirements end-up making records work pretty much like structs, except they use tuples underneath (which aren’t that much faster anyway and have their own downsides), then it is worth asking what is the point anyway.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="160019" data-batch-url="/posts/batch_likers">
                        2
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/decimal-1-9-and-2-0/28438/18">Post #17</a>
	                </div>
	            </div>
              <div id="likers-container-160019" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="160019"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #17"></div>
  </section>
</div>
    <div class="postbit" id="160021" data-post-id="160021">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="OvermindDL1" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/OvermindDL1/120/2677_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  OvermindDL1
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>That’s why I think the restrictions are good.  Because there can be multiple type of records with the same signature (same first element atom and same number and potentially even type of fields) is specifically why they need to be restricted.  It will rule out overlapping records but that’s just part of the ‘interface definition’ of the protocols.  If they don’t want that restriction then they should use something else that would be able to distinguish between signature identical records (like protocol_ex).  For the most general use these restrictions would be far more than sufficient as they would handle the general case where people would want to use them.  As you stated the <code>__struct__</code> key in struct’s is just an ‘unlikely to conflict’ convention, but it’s not impossible for, say, a deserialized json map to have such a key and to suddenly be passed through a protocol and then to suddenly fail when it never failed with other deserialized data before, it is possible, but it is not likely, same thing with these records.  In general I whole-heartedly would design it in such a way that only records defined for such protocols should be allowed, not just any record without some excess support like a wrapper (which is also not a rare thing to have is wrapper structs).</p>
<p>Personally I prefer using records over structs because I prefer nominally typed structures (elixir records) over row-typed structures (elixir structs), not just because they are ~19% faster in the simple case (though can be a lot faster on larger mostly-read-only records) but because they make it impossible to accidentally pass it to row-destructured function heads (which I have accidentally done in elixir on more than a few occasions).  If Elixir were a statically typed language then it would be significantly less of an issue, but it’s specifically because elixir is so dynamically typed that matching becomes so ‘fluffy’ when we don’t necessarily want it to.</p>
<p>Plus sometimes working with tuples is just outright easier than maps.  ^.^</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="160021" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/decimal-1-9-and-2-0/28438/19">Post #18</a>
	                </div>
	            </div>
              <div id="likers-container-160021" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="160021"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #18"></div>
  </section>
</div>
    <div class="postbit" id="160030" data-post-id="160030">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="josevalim" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/josevalim/120/1787_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  josevalim
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Creator of Elixir</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="OvermindDL1" data-post="19" data-topic="28438">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/overminddl1/48/2677_2.png" class="avatar"> OvermindDL1:</div>
<blockquote>
<p>That’s why I think the restrictions are good.</p>
</blockquote>
</aside>
<p>Right. I am not disagreeing with this. My only point is if you add restrictions, then they are not records as they are known today.</p>
<aside class="quote no-group" data-username="OvermindDL1" data-post="19" data-topic="28438">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/overminddl1/48/2677_2.png" class="avatar"> OvermindDL1:</div>
<blockquote>
<p>As you stated the <code>__struct__</code> key in struct’s is just an ‘unlikely to conflict’ convention, but it’s not impossible for, say, a deserialized json map to have such a key</p>
</blockquote>
</aside>
<p>If this happens, it is a bug in the JSON decoding code, because it has obvious security implications. AFAIK, jason and poison do not allow it.</p>
<p>The problem is not even about JSON or external formats. Just to provide a concrete example, when we supported protocols for records in Elixir, we would have bugs when inspecting Macro.Env. That’s because Macro.Env has fields like <code>{SomeModule, [some_function: 1]}</code> and if <code>SomeModule</code> defines a record of one element, Inspect would be called with the wrong value, and everything would fail. So the claim that the chance for conflicts in records is the same as structs is not true, it is much more common. If Elixir was statically typed then we could rule out based on typing, but Elixir isn’t - and it isn’t the point of this discussion either.</p>
<aside class="quote no-group" data-username="OvermindDL1" data-post="19" data-topic="28438">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/overminddl1/48/2677_2.png" class="avatar"> OvermindDL1:</div>
<blockquote>
<p>because they make it impossible to accidentally pass it to row-destructured function heads</p>
</blockquote>
</aside>
<p>In order to use records, you would always have to type the record name. And if you always add the struct name, this issue doesn’t exist either. The row-destructure feature from structs can be useful in certain occasions and fully removing it is actually one extra downside in records.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="160030" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/decimal-1-9-and-2-0/28438/20">Post #19</a>
	                </div>
	            </div>
              <div id="likers-container-160030" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="160030"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #19"></div>
  </section>
</div>
    <div class="postbit" id="160038" data-post-id="160038">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="OvermindDL1" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/OvermindDL1/120/2677_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  OvermindDL1
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote group-livebook_core_team" data-username="josevalim" data-post="20" data-topic="28438">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/josevalim/48/1787_2.png" class="avatar"> josevalim:</div>
<blockquote>
<p>Right. I am not disagreeing with this. My only point is if you add restrictions, then they are not records as they are known today.</p>
</blockquote>
</aside>
<p>Correct, and I don’t think they really need to be.  Records lost a lot of their power when tuple calls were removed from the BEAM so they are a fraction of even what they used to be.</p>
<aside class="quote group-livebook_core_team" data-username="josevalim" data-post="20" data-topic="28438">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/josevalim/48/1787_2.png" class="avatar"> josevalim:</div>
<blockquote>
<p>AFAIK, jason and poison do not allow it.</p>
</blockquote>
</aside>
<p>Why would they disallow completely legal and to the spec JSON however?  At worst they should keep the key as a string instead of converting it to an existing atom when that option is requested.</p>
<aside class="quote group-livebook_core_team" data-username="josevalim" data-post="20" data-topic="28438">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/josevalim/48/1787_2.png" class="avatar"> josevalim:</div>
<blockquote>
<p>Just to provide a concrete example, when we supported protocols for records in Elixir, we would have bugs when inspecting Macro.Env. That’s because Macro.Env has fields like <code>{SomeModule, [some_function: 1]}</code> and if <code>SomeModule</code> defines a record of one element, Inspect would be called with the wrong value, and everything would fail. So the claim that the chance for conflicts in records is the same as structs is not true, it is much more common.</p>
</blockquote>
</aside>
<p>That’s precisely why I think the limitations of requiring an implementation for the protocol should be required, else they are handled like normal tuples.  If there is no implementation defined for the record, even if there is a record, it should still fall back to normal tuple handling (which is the opposite of structs, which default to error’ing instead of normal map handling).</p>
<aside class="quote group-livebook_core_team" data-username="josevalim" data-post="20" data-topic="28438">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/josevalim/48/1787_2.png" class="avatar"> josevalim:</div>
<blockquote>
<p>The row-destructure feature from structs can be useful in certain occasions and fully removing it is actually one extra downside in records.</p>
</blockquote>
</aside>
<p>Row-destructuring is absolutely a feature, but it’s not one that is always wanted.  That is why I can only think of a single language that has row-typed records that doesn’t have nominally-typed records, that being Elm (which is a fairly low level wrapper around javascript’s object’s), and it’s occasionally been a pain there.  The vast majority of languages that have row-typed records also have nominally-typed records, because they serve different purposes.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="160038" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/decimal-1-9-and-2-0/28438/21">Post #20</a>
	                </div>
	            </div>
              <div id="likers-container-160038" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="160038"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #20"></div>
  </section>
</div>
</template></turbo-stream><turbo-stream action="replace" target="load-more-container"><template><div id="load-more-container" class="load-more-container">
    <a class="load-more-button" data-turbo-stream="true" href="/topics/28438/load_more?page=3">Load more posts (5 remaining)</a>
</div></template></turbo-stream>