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


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

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Just made a reduced example that works wonderfully.</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule Song do
  @opaque title :: binary
  @spec title!(binary) :: title()
  def title!("" &lt;&gt; t), do: t

  @opaque artist :: binary
  @spec artist!(binary) :: artist()
  def artist!("" &lt;&gt; a), do: a

  @type t :: %{artist: artist, title: title}
  @spec create(artist, title) :: t()
  def create(artist, title), do: %{artist: artist, title: title}
end

defmodule Test do
  def test() do
    # No error:
    Song.create(Song.artist!("Artist"), Song.title!("Title"))
    # Wrong order of arguments causes Dialyzer error:
    Song.create(Song.title!("Title"), Song.artist!("Artist"))
  end
end
</code></pre>
<p>Edit: Song.t() can be a normal type, no need for it to be opaque.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="247492" 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/zero-cost-abstraction-for-newtypes-in-elixir/46636/22">Post #21</a>
	                </div>
	            </div>
              <div id="likers-container-247492" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="247492"
                     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 #21"></div>
  </section>
</div>
    <div class="postbit" id="247493" data-post-id="247493">
  <section>
    <div class="post-wrap">


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

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Just to make it obvious (because I was fooled by it) <a class="mention" href="/u/tomekowal" rel="nofollow">@tomekowal</a>’s code causes the dialyzer errors for actually mismatched types, not for actually correct code. I totally didn’t see the comment in <code>Test.test/0</code>.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="247493" 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/zero-cost-abstraction-for-newtypes-in-elixir/46636/23">Post #22</a>
	                </div>
	            </div>
              <div id="likers-container-247493" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="247493"
                     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 #22"></div>
  </section>
</div>
    <div class="postbit" id="247519" data-post-id="247519">
  <section>
    <div class="post-wrap">


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

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Before you jump around euphorically and introduce this idea in your code base, here are some issues you should be aware of:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule Zero_Cost_Type_Wrapper_Issues do
  def issues() do
    song = Song.create(Song.artist!("Artist"), Song.title!("Title"))

    # warning Function issues/0 has no local return
    _issue1 = "#{inspect(song.title)} by #{song.artist}"
    _fix1a = "#{inspect(song.title)} by #{as_string(song.artist)}"
    _fix1b = "#{inspect(song.title)} by #{song.artist &lt;&gt; ""}"

    case song.title do
      # warning The attempt to match a term of type
      #                   'Elixir.Song':title() against the pattern
      #                   &lt;&lt;65,_/binary&gt;&gt; breaks the opacity of the term
      "A" &lt;&gt; _issue2 -&gt; :A
      _ -&gt; nil
    end

    # Fix:
    case as_string(song.title) do
      "A" &lt;&gt; _fix2 -&gt; :A
      _ -&gt; nil
    end
  end

  @spec as_string(any) :: binary
  def as_string(s), do: s
end
</code></pre>
<p>These are 2 issues I could discover immediately. It’s very likely there are a few others. Another solution is to simply use <code>binary</code> as the type of the struct/map attributes and only give the opaque types to the function parameters.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="247519" 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/zero-cost-abstraction-for-newtypes-in-elixir/46636/24">Post #23</a>
	                </div>
	            </div>
              <div id="likers-container-247519" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="247519"
                     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 #23"></div>
  </section>
</div>
    <div class="postbit" id="247735" data-post-id="247735">
  <section>
    <div class="post-wrap">


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

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Yeah, that is the strength of opaque type. It enforces proper encapsulation. Basically, you cannot rely anymore on the type being binary. You need <code>song!\1</code> or <code>Song.new\1</code> to tell Dialyzer "I am explicitly converting <code>binary</code> to <code>song</code>. Then, you’d have to do the other way round. <code>Song.get(song) :: binary</code>. That’s why I’d rather define each opaque type as its own separate module. You’ll need constructor and getters.</p>
<p>It is great when you have a struct with private fields that you want to ensure, that nobody else uses. Or if you think the internal implementation can change (e.g. using map vs keyword list) and you don’t want users to rely on it. It is also great when you have some kind of “handle” that can be a PID or an in-memory struct.</p>
<p>However, it is not exactly the same as zero-cost abstraction in other langs.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="247735" 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/zero-cost-abstraction-for-newtypes-in-elixir/46636/25">Post #24</a>
	                </div>
	            </div>
              <div id="likers-container-247735" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="247735"
                     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 #24"></div>
  </section>
</div>
    <div class="postbit" id="247752" data-post-id="247752">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="Fl4m3Ph03n1x" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/Fl4m3Ph03n1x/120/11709_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  Fl4m3Ph03n1x
                      <span class="op-star" title="Thread Starter">
                        <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                      </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>After reading more I finally came up with a small macro that can be used as a proof of concept, a trivial implementation of the <code>NewType</code> in Elixir.</p>
<p>While it is not a zero cost abstraction, it is very lightweight and  I seriously doubt anyone would run into considerable performance issues using it.</p>
<p>I expanded over the idea of <a class="mention" href="/u/tomekowal" rel="nofollow">@tomekowal</a> and used tuples, mainly to check for type validation. I am sure there is another way of doing the same <code>is_type?/2</code> function without using tuples, but I think this is fast enough and the code is clear enough, so no harm done:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule NewType do
  defmacro deftype(name, type) do
    quote do
      defmodule unquote(name) do
        @opaque t :: {unquote(name), unquote(type)}

        @spec new(value :: unquote(type)) :: t
        def new(value), do: {unquote(name), value}

        @spec extract(new_type :: t) :: unquote(type)
        def extract({unquote(name), value}), do: value
      end
    end
  end

  @spec is_type?(data :: {atom, any}, new_type :: atom) :: boolean
  def is_type?({type, _data}, new_type) when type == new_type, do: true
  def is_type?(_data, _new_type), do: false
end
</code></pre>
<p>To see the full context:</p>
<aside class="quote quote-modified" data-post="10" data-topic="46852">
  <div class="title">
    <div class="quote-controls"></div>
    <img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/fl4m3ph03n1x/48/11709_2.png" class="avatar">
    <div class="quote-title__text-content">
      <a href="https://forum.elixirforum.com/t/how-to-define-macro-for-a-new-type/46852/10" rel="nofollow">How to define Macro for a new Type?</a> <a class="badge-category__wrapper " href="/c/questions-help/questions/53" rel="nofollow"><span data-category-id="53" style="--category-badge-color: #C14BFB; --category-badge-text-color: #000000; --parent-category-badge-color: #C14BFB;" data-parent-category-id="171" data-drop-close="true" class="badge-category --style-square --has-parent" title="Elixir Questions / Help"><span class="badge-category__name">Questions</span></span></a>
    </div>
  </div>
  <blockquote>
    <a name="p-247750-my-answer-1" class="anchor" href="#p-247750-my-answer-1" aria-label="Heading link" rel="nofollow"></a>My Answer
After reading more about macros in Elixir, talking to the community and reading about  NewType, I have refined my ideas. While the exact implementation of my original idea is not possible, with some changes you can still get the core benefit of NewType. 
<a name="p-247750-changes-to-original-idea-2" class="anchor" href="#p-247750-changes-to-original-idea-2" aria-label="Heading link" rel="nofollow"></a>Changes to original idea

No usage of Name("John") syntax. As explained in <a href="https://forum.elixirforum.com/t/how-to-define-macro-for-a-new-type/46852/2" rel="nofollow">this post</a> this syntax is <a href="https://hexdocs.pm/elixir/syntax-reference.html" rel="noopener nofollow ugc">not valid</a> in Elixir.
No defguard. Because the type is @opaque it is not possible to have a guard that analyses the internal structure o…
  </blockquote>
</aside>
 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="247752" 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/zero-cost-abstraction-for-newtypes-in-elixir/46636/26">Post #25</a>
	                </div>
	            </div>
              <div id="likers-container-247752" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="247752"
                     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 #25"></div>
  </section>
</div>
    <div class="postbit" id="247839" data-post-id="247839">
  <section>
    <div class="post-wrap">


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

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Personally I do not see much value in this over just using a tagged tuple directly.</p>
<p>Though it seems you want to have it like this, as it suites some usecase you have, I am totally fine with that.</p>
<p>Still I have some nitpick about the <code>is_type?/2</code> predicates naming. <code>has_</code> and <code>is_</code> prefixes are only used for guards, while <code>?</code> suffix is left out for guards. As you have a non guard function, you should therefore name it <code>type?</code>.</p>
<p>Also the function might be more idiomatically written by fully using pattern match rather than quards:</p>
<pre data-code-wrap="ex"><code class="lang-ex">  @spec is_type?(data :: {atom, any}, new_type :: atom) :: boolean
  def is_type?({type, _data}, type), do: true
  def is_type?(_data, _new_type), do: false
</code></pre>
<p>Please be also aware, that calling the predicate with any <code>@opaque</code> type will make dialyzer complain.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="247839" 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/zero-cost-abstraction-for-newtypes-in-elixir/46636/27">Post #26</a>
	                </div>
	            </div>
              <div id="likers-container-247839" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="247839"
                     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 #26"></div>
  </section>
</div>
    <div class="postbit" id="247843" data-post-id="247843">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="Fl4m3Ph03n1x" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/Fl4m3Ph03n1x/120/11709_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  Fl4m3Ph03n1x
                      <span class="op-star" title="Thread Starter">
                        <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                      </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="NobbZ" data-post="27" data-topic="46636">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/nobbz/48/27235_2.png" class="avatar"> NobbZ:</div>
<blockquote>
<p>Still I have some nitpick about the <code>is_type?/2</code> predicates naming. <code>has_</code> and <code>is_</code> prefixes are only used for guards, while <code>?</code> suffix is left out for guards. As you have a non guard function, you should therefore name it <code>type?</code>.</p>
</blockquote>
</aside>
<p>What about <a href="https://hexdocs.pm/elixir/1.12.3/Map.html#has_key?/2" rel="noopener nofollow ugc">Map.has_key?</a><br>
I do like you suggestion though <img src="https://forum.elixirforum.com/images/emoji/apple/smiley.png?v=15" title=":smiley:" class="emoji" alt=":smiley:" loading="lazy" width="20" height="20"></p>
<aside class="quote no-group" data-username="NobbZ" data-post="27" data-topic="46636">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/nobbz/48/27235_2.png" class="avatar"> NobbZ:</div>
<blockquote>
<p>Also the function might be more idiomatically written by fully using pattern match rather than quards:</p>
</blockquote>
</aside>
<p>Agreed !</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="247843" 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/zero-cost-abstraction-for-newtypes-in-elixir/46636/28">Post #27</a>
	                </div>
	            </div>
              <div id="likers-container-247843" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="247843"
                     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 #27"></div>
  </section>
</div>
    <div class="postbit" id="247846" data-post-id="247846">
  <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">
								<p>This is quite an interesting conversation. I very much like the example with opaque Dialyzer types which <a class="mention" href="/u/tomekowal" rel="nofollow">@tomekowal</a> provided.</p>
<p>One thing that might be tried, is adding <code>@compile :inline</code> to the module(s) in which these functions (like <code>Song.new/1</code> and <code>Song.get/1</code>) are used. This might* convince the Erlang compiler to inline their definitions, which would truly make them “zero cost” at runtime.</p>
<p><small>*: The rules for when particular functions are and aren’t inlined are a bit vague. Local functions are very easily inlined. Remote functions only sometimes, and I am not entirely sure when (<a href="https://www.erlang.org/doc/man/compile.html#inlining" rel="nofollow">the documentation</a> is not very clear on this, or rather, what I see in practice does not seems to always follow the documentation. If some expert can shed light on it, I’d be very grateful!)</small></p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="247846" 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/zero-cost-abstraction-for-newtypes-in-elixir/46636/29">Post #28</a>
	                </div>
	            </div>
              <div id="likers-container-247846" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="247846"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-last-post cat-last-post" title="Last post!"></div>
  </section>
</div>
</template></turbo-stream><turbo-stream action="replace" target="load-more-container"><template><div id="load-more-container" class="load-more-container">
    <span class="all-loaded">— All posts loaded —</span>
</div></template></turbo-stream>