<turbo-stream action="append" target="posts_list"><template>    <div class="postbit" id="367281" data-post-id="367281">
  <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">
								<p><a class="mention" href="/u/smueller" rel="nofollow">@smueller</a> thank you. Btw, please don’t use type inference in the examples, as the goal is to validate the type syntax. There are several cases where it can be omitted in signatures - not all - but when exploring the syntax it should be done fully.</p>
<p>Even languages with complete inference, many teams prefer to explicitly type all signatures. If the syntax requires inference to be palatable, then that’s not a good sign.</p>
<p>EDIT: also note you cannot use the <code>foo: Type</code> as that is already available today for pattern matching in keyword lists. Such change would be ambiguous and backwards incompatible.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="367281" 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/a-case-for-inline-type-annotations/71220/53">Post #52</a>
	                </div>
	            </div>
              <div id="likers-container-367281" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="367281"
                     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 #52"></div>
  </section>
</div>
    <div class="postbit" id="367296" data-post-id="367296">
  <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">
								<p>After sleeping on it, here is my criticism of inline annotations.</p>
<h2><a name="p-367296-too-much-repetition-1" class="anchor" href="#p-367296-too-much-repetition-1" aria-label="Heading link" rel="nofollow"></a>Too much repetition</h2>
<p>For example, take <code>encode_kv_pair</code>. The different clauses are effectively variations of the same type, which are repeated over and over again:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">  defp encode_kv_pair({key, _}: QueryPair, _encoding: EncodingType) -&gt; no_return when is_list(key)
  defp encode_kv_pair({_, value}: QueryPair, _encoding: EncodingType) -&gt; no_return when is_list(value)
  defp encode_kv_pair({key, value}: QueryPair, :rfc3986) -&gt; String
  defp encode_kv_pair({key, value}: QueryPair, :www_form) -&gt; String
</code></pre>
<p>In the example above, there was even a need to rely on inference to reduce some of the repetition. With a separate clause, the type signature is defined once:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">  $ query_pair(), encoding_type() -&gt; string()
  defp encode_kv_pair({key, _}, _encoding) when is_list(key)
  defp encode_kv_pair({_, value}, _encoding) when is_list(value)
  defp encode_kv_pair({key, value}, :rfc3986)
  defp encode_kv_pair({key, value}, :www_form)
</code></pre>
<p>While I definitely prefer the second one, and some of it can be described as taste, there is no discussion the second one is more concise and less repetitive. You can see this happening over and over when comparing snippets, in almost every function that has more than one clause.</p>
<h2><a name="p-367296-it-obscures-the-actual-signature-2" class="anchor" href="#p-367296-it-obscures-the-actual-signature-2" aria-label="Heading link" rel="nofollow"></a>It obscures the actual signature</h2>
<p>One of the benefits of type signatures is to provide a brief description of what the function does. However, if you only have inline type annotations, this can become very hard. For example, let’s look at your <code>merge</code> function and have it with the actual code:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">  def merge(%URI{scheme: nil} = uri, _rel: t | String) -&gt; no_return do
    raise ArgumentError, "you must merge onto an absolute URI"
  end

  def merge(base: t | String, %URI{scheme: rel_scheme} = rel) -&gt; t when rel_scheme != nil do
    %{rel | path: remove_dot_segments_from_path(rel.path)}
  end

  def merge(%URI{} = base, %URI{host: host} = rel) -&gt; t when host != nil  do
    %{rel | scheme: base.scheme, path: remove_dot_segments_from_path(rel.path)}
  end

  def merge(%URI{} = base, %URI{path: nil} = rel) -&gt; t  do
    %{base | query: rel.query || base.query, fragment: rel.fragment}
  end

  def merge(%URI{host: nil, path: nil} = base, %URI{} = rel) -&gt; t do
    %{
      base
      | path: remove_dot_segments_from_path(rel.path),
        query: rel.query,
        fragment: rel.fragment
    }
  end

  def merge(%URI{} = base, %URI{} = rel) -&gt; t do
    new_path = merge_paths(base.path, rel.path)
    %{base | path: new_path, query: rel.query, fragment: rel.fragment}
  end
  
  def merge(base: String, rel: String) -&gt; t do
    merge(parse(base), parse(rel))
  end
</code></pre>
<p>It is really hard to tell the arguments it receives and the expected return types. You need to go clause by clause, build which one overlaps and which ones do not in our head. Maybe each of them handle a different type, maybe not.</p>
<p>However, if you have a separate type declaration at the top, regardless of the syntax, then it is immediately clear:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">  def merge(base_uri: t | String, relative_uri: t | String) -&gt; t
  
  # or
  
  $ t | String, t | String -&gt; t
  def merge(uri_or_string, uri_or_string)
</code></pre>
<h2><a name="p-367296-incorrect-type-annotations-3" class="anchor" href="#p-367296-incorrect-type-annotations-3" aria-label="Heading link" rel="nofollow"></a>Incorrect type annotations</h2>
<p>Some of your examples have clearly invalid type annotations. Let’s keep annotation and code together once more:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">  defp merge_paths(base_path: nil, rel_path: String?) -&gt; String? do
    merge_paths("/", rel_path)
  end

  defp merge_paths(base_path: String?, "/" &lt;&gt; _ = rel_path: String?) -&gt; String? do
    remove_dot_segments_from_path(rel_path)
  end

  defp merge_paths(base_path: String?, rel_path: String?) -&gt; String? do
    (path_to_segments(base_path) ++ [:+] ++ path_to_segments(rel_path))
    |&gt; remove_dot_segments([])
    |&gt; join_reversed_segments()
  end
</code></pre>
<p>The second clause says <code>"/" &lt;&gt; _ = rel_path: String?</code> but it clearly cannot handle nils. The last clause says it deals with nils, but it does not. Here is another one:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">  # First argument doesn't deal with all Segments, only with part of them (empty lists)
  defp remove_dot_segments([]: Segments, acc: Segments) -&gt; Segments
</code></pre>
<p>We see a similar mistakes in the return types to <code>split_authority</code> and <code>hex_to_dec</code>. Furthermore, because you were relying on inference, many of these mistakes have been hidden. If you fully type the arguments, you will see this popping up more and more.</p>
<p>Of course, the type system would find these bugs in practice, but the fact those issues are popping so frequently shows there are fundamental semantic inconsistencies with inline type annotations, which we will explore next.</p>
<h2><a name="p-367296-type-aliases-awkwardness-4" class="anchor" href="#p-367296-type-aliases-awkwardness-4" aria-label="Heading link" rel="nofollow"></a>Type aliases awkwardness</h2>
<p>Inference is a great feature to have and most of our work so far has been on inference. However, many teams on statically typed languages prefer to rely on inference as little as possible. Especially because inference may hide bugs in certian cases. For example, you chose to rely on inference for <code>decode_with_encoding</code>, to avoid repetition:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">  defp decode_with_encoding(string: String, :www_form) -&gt; String
  defp decode_with_encoding(string: String, :rfc3986) -&gt; String
</code></pre>
<p>However, this clause has one issue: if you change <code>EncodingType</code> to have a new entry, you won’t have a typing violation in this function, because nowhere you defined it is supposed to handle all <code>EncodingType</code>s. While in this case you will likely get a warning anyway, because it is all defined in the same module, you won’t be able to rely on inference when implementing clauses for a type alias defined in another module (as I showed in <code>log_payment_status</code> earlier).</p>
<p>Of course, the answer would be to annotate those types:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">  defp decode_with_encoding(string: String, :www_form: EncodingType) -&gt; String
  defp decode_with_encoding(string: String, :rfc3986: EncodingType) -&gt; String
</code></pre>
<p>However, per the previous section, the definition above is invalid. A type alias means, by definition, that if you have <code>typealias Alias = Type1 or Type2</code>, you can replace the <code>Alias</code> by <code>Type1 or Type2</code>. That’s how they behave in all typed programming languages. This means you literally wrote this signature:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">  defp decode_with_encoding(string: String, :www_form: :www_form | :rfc3986) -&gt; String
  defp decode_with_encoding(string: String, :rfc3986: :www_form | :rfc3986) -&gt; String
</code></pre>
<p>which, per above, is clearly wrong.</p>
<p>This puts us in a pickle. If type inference won’t help us catch type alias changing and we cannot use the type alias, <strong>there is literally nowhere we can annotate that this function is meant to handle all <code>EncodingType</code>, unless we define the type annotation separately</strong>. This is what Wojtek and I referred to earlier in this thread in the <code>hostname</code> function. The issue was not the syntax, the issue is that <strong>inline annotations are semantically at odds with pattern matching</strong>, which is accentuated by type aliases.</p>
<h2><a name="p-367296-summary-5" class="anchor" href="#p-367296-summary-5" aria-label="Heading link" rel="nofollow"></a>Summary</h2>
<p>There are a couple other issues with your inline type annotations, such as the syntax being fundamentally incompatible (anyone who disagrees is welcome to change the parser and prove me wrong), but hopefully the above is enough to show they have enough syntactical and semantic issues when typing existing code. And that’s within a single module! The Elixir repository has 447 modules and over 5700 public functions, while the URI module has 29 of them. So these issued popped up when typing 0.5% of a single codebase.</p>
<p>But how can we be certain that having type annotations apart is better? Well, that’s how we have been adding annotations for the last 10+ years via typespecs, and new type system is meant to improve on the flaws of the existing typespecs. I acknowledge there are a separate discussion to have about syntax, in this thread someone already asked about parens being required or optional, or even the <code>$</code> of itself, but it is clear having type annotations separate from clauses is the superior choice.</p>
<p>Finally, I have to say it is a bit frustrating that at no moment none of the cons above have been mentioned, posing inline type annotations as having only benefits and no trade-offs. In particular, I disagree with almost all items from your summary:</p>
<blockquote>
<ul>
<li><strong>No mental mapping</strong> of positional types to parameters</li>
</ul>
</blockquote>
<p>I actually agree with this one, having to positionally map types to arguments is one of the downsides of having them separate.</p>
<blockquote>
<ul>
<li><strong>Self-documenting</strong> function signatures</li>
</ul>
</blockquote>
<p>As shown above, that’s clearly false. Whenever a function has more than one clause, I need to parse through every single annotation to figure out the types it accepts and returns. The combination of inline annotations with type inference in your latest snippet only makes this harder and defeats any self-documenting purpose.</p>
<blockquote>
<ul>
<li><strong>Easier refactoring</strong> - types move with parameters</li>
</ul>
</blockquote>
<p>I disagree. While inline annotations makes it easier to move code to a new place, it comes with the huge downside that you can break code when you move it around, because you change the specification and implementation at the same time. Given the purpose of types is to help us find bugs, I’d rather err on making sure bugs do not go undetected.</p>
<blockquote>
<ul>
<li><strong>Better IDE support</strong> potential for inline type information</li>
</ul>
</blockquote>
<p>I disagree. I see no reason why IDEs would struggle with any of the approaches.</p>
<blockquote>
<ul>
<li><strong>Pattern-aware</strong> - leverages Elixir’s existing pattern matching strengths</li>
</ul>
</blockquote>
<p>Strongly disagree. Mixing inline annotations with pattern matching leads to excessive typing violations, as explained at length above, especially when aliases are used. And when relying on type inference to avoid repetition, as done above, allows bugs to creep in.</p>
<p>Still, I appreciate the time for this discussion because I know others would have the same questions. Now we can hopefully put this particular topic past us and focus on approaches more suitable to the language. Thank you.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="367296" data-batch-url="/posts/batch_likers">
                        29
                      </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/a-case-for-inline-type-annotations/71220/54">Post #53</a>
	                </div>
	            </div>
              <div id="likers-container-367296" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="367296"
                     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 #53"></div>
  </section>
</div>
    <div class="postbit" id="367314" data-post-id="367314">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="garrison" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  garrison
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="smueller" data-post="1" data-topic="71220">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/smueller/48/3857_2.png" class="avatar"> smueller:</div>
<blockquote>
<p>Types belong next to the values they describe. Splitting types from the function definition forces developers to mentally reconcile two separate lines:</p>
</blockquote>
</aside>
<p>I think this is the best argument in favor of inlining the types (I don’t find the others convincing). In particular, when <em>updating</em> a function signature it can be easy to accidentally forget to update the corresponding type signature. This happens to me with reasonable frequency.</p>
<p>However, this is something which can be solved with tooling. In practice the compiler catches almost all of these immediately (if the arity doesn’t match) and I see an error via ElixirLS. For more complex (typing) mismatches Dialyzer will generally catch them, and again the LSP will inline a warning.</p>
<p>Given that the main benefit of the inline syntax would be to catch an arity mismatch, and given that the existing tooling already catches those 100% of the time before the code is even run, I don’t think it’s a big deal. The new type system (and new LSP) will only improve this further.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="367314" 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/a-case-for-inline-type-annotations/71220/55">Post #54</a>
	                </div>
	            </div>
              <div id="likers-container-367314" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="367314"
                     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 #54"></div>
  </section>
</div>
    <div class="postbit" id="367372" data-post-id="367372">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="matt-savvy" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  matt-savvy
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>It’s pretty surprising how readable this version is.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="367372" 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/a-case-for-inline-type-annotations/71220/56">Post #55</a>
	                </div>
	            </div>
              <div id="likers-container-367372" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="367372"
                     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 #55"></div>
  </section>
</div>
    <div class="postbit" id="367645" data-post-id="367645">
  <section>
    <div class="post-wrap">


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

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>I apologize if I intrude to give the type-theorist viewpoint. José knows I am quite fond of having inline type annotations (if used with parsimony), but not at the expense of whole type annotations. Giving the whole type annotation is far more expressive than just typing function parameters (besides all the advantages already evoked in this thread). The simplest example I can think of is the or function <code>or</code> as it is defined in JavaScript: it takes two arguments and returns the first one if it is truthy, otherwise it returns the second one. In Elixir, we can write this as follows (assuming that falsy values are 0, “”, 0.0, and :false):</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">def or(x,y) do
  if x != 0 and x!= "" and x != 0.0 and x != false do
    x
  else
    y
  end
end
</code></pre>
<p>You can define a type annotation that precisely describes the function:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">$ type Falsy = 0 or "" or 0.0 or :false
$ type Truthy = not Falsy

$ ((Falsy, a) -&gt; a) and ((Truthy and b, term()) -&gt; (Truthy and b)) 
  when a: term(), b: term()
</code></pre>
<p>where a and b are type variables.</p>
<p>I do not pretend that the type above is readable, but it exactly states what I wrote in English: “the function returns its second argument if the first one is falsy, otherwise it returns the first argument”. While this is not implemented in Elixir (yet?), we have running prototypes that can reconstruct this type for the unannotated code [see POPL25 conference]. There is no way to obtain such precision (crucial to precisely track types in branching) by typing the function parameters (unless, of course, you use two distinct function clauses, which is not the point).<br>
The only type we can give to the parameters x and y is <code>term()</code> therefore deducing for <code>or</code> the type <code>term(), term() -&gt; term()</code>, the one of all binary functions.</p>
<p>To summarize, whole type annotations are necessary to fully exploit the expressiveness of set-theoretic types.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="367645" data-batch-url="/posts/batch_likers">
                        12
                      </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/a-case-for-inline-type-annotations/71220/57">Post #56</a>
	                </div>
	            </div>
              <div id="likers-container-367645" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="367645"
                     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 #56"></div>
  </section>
</div>
    <div class="postbit" id="372156" data-post-id="372156">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="smueller" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/smueller/120/3857_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  smueller
                    <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>Hi <a class="mention" href="/u/josevalim" rel="nofollow">@josevalim</a>, I’ve been meaning to respond to your post. I wanted to give it more thought, then the summer got ultra busy.</p>
<aside class="quote group-livebook_core_team" data-username="josevalim" data-post="54" data-topic="71220">
<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 have to say it is a bit frustrating that at no moment none of the cons above have been mentioned, posing inline type annotations as having only benefits and no trade-offs</p>
</blockquote>
</aside>
<p>The cons of my proposal is that inline type annotations can get verbose, especially when going wild with pattern matching. I see inline types vs decorators as analogous to javascript’s JSDoc vs Typescript:</p>
<ul>
<li>JSDoc has advantages for library creators (see Rich Harris’ thoughts, creator of Svelte)</li>
<li>Typescript is largely recommended for codebases</li>
</ul>
<p>(And this may also clue us in to our differences in opinions <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>I propose a compromise – a hybrid approach merging both worlds (and along the lines of what <a class="mention" href="/u/lud" rel="nofollow">@lud</a> proposed):</p>
<pre data-code-wrap="elixir"><code class="lang-elixir"># current direction:
$ query_pair(), encoding_type() -&gt; string()
defp encode_kv_pair({key, _}, _encoding) when is_list(key), do: ...

# hybrid approach:
type encode_kv_pair(QueryPair, EncodingType) -&gt; String
defp encode_kv_pair({key, _}, _encoding) when is_list(key), do: ...
</code></pre>
<p>This syntax is highly readable and does not require a computation to align params/return with respective types. Plus it is a straightforward line to eventually introduce inline type annotations as well, with little to no mental model shift. This would IMHO be the holy grail of elixir, and naturally complement the common coding pattern of simple → complex, e.g.:</p>
<ul>
<li>Developer starts simple, creates the base function <code>defp encode_kv_pair(QueryPair, EncodingType) -&gt; String</code></li>
<li>As complexity grows and the function introduces pattern matching / overloads, the type refactors out to an annotation (with 4 charters or less in changes)</li>
</ul>
<p>Thoughts?</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="372156" 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/a-case-for-inline-type-annotations/71220/58">Post #57</a>
	                </div>
	            </div>
              <div id="likers-container-372156" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="372156"
                     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 #57"></div>
  </section>
</div>
    <div class="postbit" id="372178" data-post-id="372178">
  <section>
    <div class="post-wrap">


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

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>This discussion is starting to get old, and I have yet to see a good argument for inline types, but if there was ever one it would be that all functions would become arity one, then you might ask why is that good. Well for once, a function with lower arity is faster then one with many and there is a limit to how many arity you can have.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="372178" 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/a-case-for-inline-type-annotations/71220/59">Post #58</a>
	                </div>
	            </div>
              <div id="likers-container-372178" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="372178"
                     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 #58"></div>
  </section>
</div>
    <div class="postbit" id="372204" data-post-id="372204">
  <section>
    <div class="post-wrap">


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

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>FWIW I’m ok with the current proposal, I think theres plenty justification for having them above and it won’t really make a big deal to have them above. But I’m not sure why no one has brought up, or maybe I missed it, a hybrid approach of inference and inline together (as opposed to the above hybrid approach), using Jose’s example could be done as:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">def log_payment_status(:trail) do
  ...
end

def log_payment_status({:success, metadata :: Metadata.t()}) do
  ...
end

def log_payment_status({:overdue, metadata :: Metadata.t()}) do
  ...
end
</code></pre>
<p>Correct me if I’m wrong, but we only ever need to annotate variables defined within the pattern match, correct? (I think theres some overlap with using <code>::</code> with the binary definition but with that aside I think my example is clear). And that can be shortened up with type aliases to be more succinct. The rest of the data structure can be inferred based upon the literals in the pattern (or guards for some variables) in order to produce at compile time the full complete type specification by adding all specifications together. Am I wrong? I think the justification for it being too noisy is still justification enough but I do worry about removal of a clause and not updating the spec to become an annoyance, even if it is warned about.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="372204" 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/a-case-for-inline-type-annotations/71220/60">Post #59</a>
	                </div>
	            </div>
              <div id="likers-container-372204" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="372204"
                     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 #59"></div>
  </section>
</div>
    <div class="postbit" id="372232" data-post-id="372232">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="smueller" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/smueller/120/3857_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  smueller
                    <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="Schultzer" data-post="59" data-topic="71220">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/schultzer/48/4339_2.png" class="avatar"> Schultzer:</div>
<blockquote>
<p>This discussion is starting to get old</p>
</blockquote>
</aside>
<p>Interesting assessment, considering type annotations could be a year or more before full release. My perspective is that we’re just getting started..</p>
<aside class="quote no-group" data-username="Schultzer" data-post="59" data-topic="71220">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/schultzer/48/4339_2.png" class="avatar"> Schultzer:</div>
<blockquote>
<p>I have yet to see a good argument for inline types</p>
</blockquote>
</aside>
<p>So you disagree with <a class="mention" href="/u/josevalim" rel="nofollow">@josevalim</a> that inline types have the advantage of providing a mental mapping of positional types to parameters?</p>
<aside class="quote no-group quote-modified" data-username="Schultzer" data-post="59" data-topic="71220">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/schultzer/48/4339_2.png" class="avatar"> Schultzer:</div>
<blockquote>
<p>[inline types would make] all functions … become arity one</p>
</blockquote>
</aside>
<p>I don’t understand these words. Can you use a different programming language as an example where inline types cause arity one, and why it’s an advantage?</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="372232" 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/a-case-for-inline-type-annotations/71220/61">Post #60</a>
	                </div>
	            </div>
              <div id="likers-container-372232" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="372232"
                     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>