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


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="giddie" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/giddie/120/34394_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  giddie
                    <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 quote-modified" data-username="dimitarvp" data-post="19" data-topic="39983" 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/dimitarvp/48/38664_2.png" class="avatar"> dimitarvp:</div>
<blockquote>
<aside class="quote no-group" data-username="giddie" data-post="16" data-topic="39983">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/giddie/48/34394_2.png" class="avatar"> giddie:</div>
<blockquote>
<p>and the additional layer of unwrapping could break existing functions that expect <code>{:ok, value}</code></p>
</blockquote>
</aside>
<p>Not sure I follow, can you give an example? I’d think that if you adopt a model or having more metadata in your result values then you’d make sure that all your project’s code is using them and then convert them at the edges of the outside world (internet) and your dependencies. Fairly standard deal when you decide to buy into a library’s value proposition.</p>
</blockquote>
</aside>
<p>Well, for example: I built this library largely out of necessity in refactoring an existing codebase. It’s definitely easier to retrofit <code>ok_then</code> into existing code that returns <code>:ok/:error</code>, because for the happy codepath the data format doesn’t actually change. If I introduce an additional wrapper for <code>:some/:none</code>, then all the code that touches the refactored functions would need to be adjusted to handle the new data format (unwrapping <code>{:some, value}</code>), which makes it tougher to make incremental changes.</p>
<p>The good news, though, is that it’s <em>really</em> easy to get <code>ok_then</code> to work with <code>:some/:none</code>, because all the generic tools are readily available (and documented):</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">@spec some_unwrap!(Result.result_input()) :: any()
def some_unwrap!(result) do
  Result.tagged_unwrap!(result, :some)
end

{:some, "hello"} |&gt; some_unwrap!()  # "hello"
:none |&gt; some_unwrap!()             # **(ArgumentError) Value is not tagged some: :none.

@spec default_some(input, (() -&gt; out) | out) :: input | {:some, out}
      when input: Result.result_input(), out: any()
def default_some(result, func_or_value) do
  Result.default_as(result, :some, func_or_value)
end

{:some, "hello"} |&gt; Result.default_some("default") # {:some, "hello"}
:none |&gt; Result.default_some("default")            # {:some, "default"}
</code></pre>
<p>There’s a <code>Result.ok_or(t, e)</code> type defined that excludes <code>:none</code>, if you want to use that in typespecs, and you can build your own functions pretty easily to simplify the double wrapping and unwrapping, if you like:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">"hello"
|&gt; Result.from_as(:some)        # {:some, "hello"}
|&gt; Result.from()                # {:ok, {:some, "hello"}}
|&gt; Result.unwrap!()             # {:some, "hello"}
|&gt; Result.tagged_unwrap!(:some) # "hello"

nil
|&gt; Result.from_as(:some)        # :none
|&gt; Result.from()                # {:ok, :none}
|&gt; Result.unwrap!()             # :none
|&gt; Result.tagged_unwrap!(:some) # **(ArgumentError) Value is not tagged some: :none.
</code></pre>
<p>I’d actually be open to adding a module in <code>ok_then</code> to support this paradigm 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="215436" 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/ok-then-the-swiss-army-knife-for-tagged-tuple-pipelines/39983/22">Post #21</a>
	                </div>
	            </div>
              <div id="likers-container-215436" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="215436"
                     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="215457" data-post-id="215457">
  <section>
    <div class="post-wrap">


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

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="giddie" data-post="22" data-topic="39983">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/giddie/48/34394_2.png" class="avatar"> giddie:</div>
<blockquote>
<p>Well, for example: I built this library largely out of necessity in refactoring an existing codebase. It’s definitely easier to retrofit <code>ok_then</code> into existing code that returns <code>:ok/:error</code>, because for the happy codepath the data format doesn’t actually change. If I introduce an additional wrapper for <code>:some/:none</code>, then all the code that touches the refactored functions would need to be adjusted to handle the new data format (unwrapping <code>{:some, value}</code>), which makes it tougher to make incremental changes.</p>
</blockquote>
</aside>
<p>I think that conflict highlights the utility of other languages having split apart Option/Maybe from Result. The former is for avoiding <code>nil</code> problems the latter for consistent error handling. So maybe there needs to be a separate module for the Option/Maybe portion with some conversion functions to allow turning them into Results and vice versa.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="215457" 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/ok-then-the-swiss-army-knife-for-tagged-tuple-pipelines/39983/23">Post #22</a>
	                </div>
	            </div>
              <div id="likers-container-215457" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="215457"
                     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="215551" data-post-id="215551">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="giddie" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/giddie/120/34394_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  giddie
                    <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="stevensonmt" data-post="23" data-topic="39983" 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/stevensonmt/48/20503_2.png" class="avatar"> stevensonmt:</div>
<blockquote>
<p>I think that conflict highlights the utility of other languages having split apart Option/Maybe from Result. The former is for avoiding <code>nil</code> problems the latter for consistent error handling. So maybe there needs to be a separate module for the Option/Maybe portion with some conversion functions to allow turning them into Results and vice versa.</p>
</blockquote>
</aside>
<p>That could be interesting, yes. There certainly would need to be more buy-in for a pattern like this, but I think we could provide support with an additional <code>Option</code> module. The significant advantage I wanted to introduce with <code>ok_then</code> was making it hard to produce <code>{:ok, nil}</code>, and a solid mechanism for falling back to default values. For this purpose, I do think <code>{:ok, value} | :none</code> works just as well as <code>{:some, value} | :none</code>.</p>
<p>The existing <code>Result</code> module also has the benefit that if code later begins returning <code>{:error, reason}</code> values too, the downstream code won’t break, since <code>Result.unwrap_or_else/2</code> will return the “else” for <em>any</em> tag that isn’t <code>:ok</code> - you might just want to add <code>Result.error_consume/2</code> somewhere to log the error. In Rust, the type would change from <code>Option&lt;T&gt;</code> to <code>Result&lt;Option&lt;T&gt;&gt;</code>, and the compiler would provide help in adjusting downstream code. We don’t get that in Elixir.</p>
<p>But yeah, for people who want to reserve Result types for <code>:ok/:error</code>, and want a separate type for <code>:some/:none</code>, it should be quite easy to add additional support. The <code>Result</code> module itself probably could be used as-is, since in theory it’ll only be used to wrap <code>Option</code>s, which should never be <code>nil</code>. I’m imagining something like this?</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">"hello"
|&gt; Option.from()                    # {:some, "hello"}
|&gt; Result.from()                    # {:ok, {:some, "hello"}}
|&gt; Result.unwrap!()                 # {:some, "hello"}
|&gt; Option.unwrap_or_else("default") # "hello"
</code></pre>
<p>And then I guess we’d want methods such as <code>Option.default</code>, <code>Option.or_else</code> etc…, pretty much identical to existing <code>Result</code> functions, but with <code>:some</code> as the happy tag instead of <code>:ok</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="215551" 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/ok-then-the-swiss-army-knife-for-tagged-tuple-pipelines/39983/24">Post #23</a>
	                </div>
	            </div>
              <div id="likers-container-215551" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="215551"
                     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="216122" data-post-id="216122">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="giddie" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/giddie/120/34394_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  giddie
                    <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>I just came across a library function that returns a very non-standard <code>list() | {:error, integer(), any()}</code>, but was pleasantly surprised to realise that <code>ok_then</code> can already handle this situation quite nicely:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">[]
|&gt; Result.tagged_retag(:untagged, :ok)                # {:ok, []}
|&gt; Result.error_map(fn {_code, reason} -&gt; reason end) # {:ok, []}

{:error, 404, "not found"}
|&gt; Result.tagged_retag(:untagged, :ok)                # {:error, 404, "not found"}
|&gt; Result.error_map(fn {_code, reason} -&gt; reason end) # {:error, "not_found"}
</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="216122" 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/ok-then-the-swiss-army-knife-for-tagged-tuple-pipelines/39983/25">Post #24</a>
	                </div>
	            </div>
              <div id="likers-container-216122" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="216122"
                     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="221089" data-post-id="221089">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="giddie" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/giddie/120/34394_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  giddie
                    <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>Version <strong>1.0.0</strong> has been released, with the following notable additions:</p>
<h4><a name="p-221089-resultfilterresult-check_function-1" class="anchor" href="#p-221089-resultfilterresult-check_function-1" aria-label="Heading link" rel="nofollow"></a>Result.filter(result, check_function)</h4>
<p>This function converts an <code>:ok</code> tuple to <code>:none</code> if a condition is not satisfied:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">iex&gt; Result.from("")                           # {:ok, ""}
...&gt; |&gt; Result.filter(&amp;String.length(&amp;1) &gt; 0)  # :none
...&gt; |&gt; Result.default("Sensible Value")
{:ok, "Sensible Value"}

iex&gt; Result.from(nil)
...&gt; |&gt; ...
{:ok, "Sensible Value"}

iex&gt; Result.from("Chosen")
...&gt; |&gt; ...
{:ok, "Chosen"}

</code></pre>
<h4><a name="p-221089-resultenummap_grouped_by_tagresults-map_function-2" class="anchor" href="#p-221089-resultenummap_grouped_by_tagresults-map_function-2" aria-label="Heading link" rel="nofollow"></a>Result.Enum.map_grouped_by_tag(results, map_function)</h4>
<p>This function allows you to map or filter all results in an enumerable that match a certain tag:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">iex&gt; [{:ok, 1}, {:error, 1}, {:ok, 2}, {:error, 2}, :none]
...&gt; |&gt; Result.Enum.map_grouped_by_tag(fn
...&gt;   :ok, values -&gt; Enum.map(values, &amp;(&amp;1 + 1))
...&gt;   :error, values -&gt; Enum.take(values, 1)
...&gt;   :none, _values -&gt; []
...&gt; end)
[{:error, 1}, {:ok, 2}, {:ok, 3}]
</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="221089" 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/ok-then-the-swiss-army-knife-for-tagged-tuple-pipelines/39983/26">Post #25</a>
	                </div>
	            </div>
              <div id="likers-container-221089" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="221089"
                     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="224345" data-post-id="224345">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="giddie" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/giddie/120/34394_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  giddie
                    <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>Version <strong>1.1.0</strong> has been released, with the following notable additions:</p>
<p><strong><code>Result.tap(result, func_or_value)</code> and related functions</strong><br>
This function behaves like <code>Kernel.tap/2</code>, but calls the function only if the result was tagged as expected:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">iex&gt; {:ok, "hello"} |&gt; Result.tap(&amp;IO.puts/1)
hello
{:ok, "hello"}
</code></pre>
<p>A 0-arity function is accepted, if you only really care about <code>:ok</code>/<code>:error</code>, rather than the value:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">iex&gt; {:ok, "hello"} |&gt; Result.tap(fn -&gt; do_something() end)
{:ok, "hello"}
</code></pre>
<p>The function is not called if the tag doesn’t match.<br>
Naturally, <code>error_tap/2</code> and <code>tagged_tap/3</code> are available too.</p>
<p><strong><code>Result.default!(result, value)</code> and family</strong><br>
If you know a default value should never be <code>nil</code>, a bang-variant of the “default” functions is now available that will raise if the value is <code>nil</code>, instead of returning <code>:none</code>:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">iex&gt; :none |&gt; Result.default!("hello")
{:ok, "hello"}

iex&gt; :none |&gt; Result.default!(nil)
** (ArgumentError) Value is nil.
</code></pre>
<p><strong><code>Result.filter/2</code> now accepts 0-arity functions and truthy values</strong><br>
Previously this function accepted only a 1-arity function, but there are situations where the value is irrelevant, or relies on an external value, and this is now possible:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">iex&gt; {:ok, "hello"} |&gt; Result.filter(fn -&gt; true end)
{:ok, "hello"}

iex&gt; {:ok, "hello"} |&gt; Result.filter(false)
:none
</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="224345" 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/ok-then-the-swiss-army-knife-for-tagged-tuple-pipelines/39983/27">Post #26</a>
	                </div>
	            </div>
              <div id="likers-container-224345" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="224345"
                     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>