<turbo-stream action="append" target="posts_list"><template>    <div class="postbit" id="317790" data-post-id="317790">
  <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">
								<aside class="quote no-group" data-username="Aduril" data-post="11" data-topic="61534">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/aduril/48/18040_2.png" class="avatar"> Aduril:</div>
<blockquote>
<p>The paradigm of LiveView encourages exactly that pattern with a socket being transformed into another socket. This pattern breaks only at the end.</p>
</blockquote>
</aside>
<p>I think this is where the mismatch comes from. Indeed in the case of <code>{:noreply, state}</code> or <code>{:ok, state}</code> it feels like you’re only transforming state. But there are in many places other options as well.</p>
<ul>
<li><code>mount/3</code> can also return <code>{:ok, state, keyword}</code></li>
<li><code>handle_call/3</code> can also return <code>{:reply, term, state}</code></li>
<li><code>handle_event/3</code> can also return <code>{:reply, map, state}</code></li>
</ul>
<p>Those callbacks are not just transformations of state, but transformations of state is just one of potentially many things they do and return information about. Sometimes those other things are even the only thing happening with no changes to state.</p>
<p>E.g. for me most simple callbacks look like this:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">def handle_event("something", _, socket) do
  socket = 
    socket
    |&gt; assign(a: :something)
    |&gt; update(:b, fn x -&gt; x + 1 end)

  {:noreply, socket}
end
</code></pre>
<p>The state transformation is neatly contained in a pipeline, but the return of that state transformation is separate to the transformation itself. It doesn’t belong in the pipeline. This becomes apparent if the code changes and you need to return a reply:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">def handle_event("something", _, socket) do
  socket = 
    socket
    |&gt; assign(a: :something)
    |&gt; update(:b, fn x -&gt; x + 1 end)

  {:reply, %{b: socket.assigns.b}, socket}
end
</code></pre>
<p><code>:noreply</code> is literally telling the caller of the callback “there’s no reply to send for this one”. That’s not a state transformation.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="317790" data-batch-url="/posts/batch_likers">
                        6
                      </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/reply-function-for-liveviews/61534/12">Post #11</a>
	                </div>
	            </div>
              <div id="likers-container-317790" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="317790"
                     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="317791" data-post-id="317791">
  <section>
    <div class="post-wrap">


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

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Agree with the sentiment of it’s up to personal preference, myself I stick to the tuple as I try to not deviate too much from the standards as it will be harder for someone new joining the project to grok all the “in-house rules”.</p>
<p>What I do personally is to have a shortcut in VSCode. So “nr” + tab becomes the no reply tuple. <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="317791" data-batch-url="/posts/batch_likers">
                        3
                      </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/reply-function-for-liveviews/61534/13">Post #12</a>
	                </div>
	            </div>
              <div id="likers-container-317791" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="317791"
                     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="317810" data-post-id="317810">
  <section>
    <div class="post-wrap">


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

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="LostKobrakai" data-post="12" data-topic="61534">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/lostkobrakai/48/3072_2.png" class="avatar"> LostKobrakai:</div>
<blockquote>
<p>I think this is where the mismatch comes from. Indeed in the case of <code>{:noreply, state}</code> or <code>{:ok, state}</code> it feels like you’re only transforming state. But there are in many places other options as well.</p>
<ul>
<li><code>mount/3</code> can also return <code>{:ok, state, keyword}</code></li>
<li><code>handle_call/3</code> can also return <code>{:reply, term, state}</code></li>
<li><code>handle_event/3</code> can also return <code>{:reply, map, state}</code></li>
</ul>
<p>Those callbacks are not just transformations of state, but transformations of state is just one of potentially many things they do and return information about. Sometimes those other things are even the only thing happening with no changes to state.</p>
</blockquote>
</aside>
<p>I was going to reply the same thing, and I think there’s a huge learning opportunity here about premature abstractions… In my experience, It’s rarely the case where your system’s data shares so many properties that you can just pipe it to infinity, but I think the main point is that you are essentially just trading one idiom for another, which is less expressive, more limited and only hides away something pretty easy to type.</p>
<p>To me at least, when I look at functions like <code>ok()</code>, <code>reply()</code>, or <code>noreply()</code> it looks like a leaky abstraction because it only hides the implementation details (if any) and it doesn’t remove enough cognitive load to justify its usage. For instance, if you are used to working with GenServers you know you can return a lot more on a <code>:noreply</code> result, so how useful is it really to abstract away a tuple by parametrizing its values?</p>
<p>All in all, I think the bigger picture is that you either get a lot of value from an abstraction that justifies its usage or you end up having an unnecessary one.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="317810" data-batch-url="/posts/batch_likers">
                        3
                      </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/reply-function-for-liveviews/61534/14">Post #13</a>
	                </div>
	            </div>
              <div id="likers-container-317810" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="317810"
                     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="317826" data-post-id="317826">
  <section>
    <div class="post-wrap">


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

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Just to add one more thing I really disliked like when I used this pattern was that any one-liner error cases (useful for small form that don’t need additional messaging than just the form error) suddenly became three lines, giving them far more visual weight than necessary.</p>
<p>What was:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">  {:error, changeset} -&gt;
    {:noreply, assign(socket, :form, to_form(changeset)}
</code></pre>
<p>became:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">  {:error, changeset} -&gt;
    socket
    |&gt; assign(:form, to_form(changeset))
    |&gt; noreply()
</code></pre>
<p>We could make it a one-liner like this:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">  {:error, changeset} -&gt;
    noreply(assign(socket, :form, to_form(changeset)))
</code></pre>
<p>which illustrates just how little value is gained here.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="317826" 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/reply-function-for-liveviews/61534/15">Post #14</a>
	                </div>
	            </div>
              <div id="likers-container-317826" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="317826"
                     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="317829" data-post-id="317829">
  <section>
    <div class="post-wrap">


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

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="Aduril" data-post="11" data-topic="61534">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/aduril/48/18040_2.png" class="avatar"> Aduril:</div>
<blockquote>
<p>I would necessarily say that the <code>reply</code> function would be a big win regarding this, but for me it feels like a small imperfection within the framework to have a ceremony at the end that serves no real benefit for most of the basic use cases</p>
</blockquote>
</aside>
<p>I love chaining pipeline myself; however, I try to limit the chaining to functions that return a value in the same shape. <code>socket</code> and <code>{:noreply, socket}</code> are not the same shape. My idiom for <code>handle_event</code> is:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">def handle_event("something", _, %Socket{assigns: %{...}} = socket) do
  {:noreply, do_something(socket, ...)}
end

defp do_something(socket, ...) do
    socket
    |&gt; assign(...)
    |&gt; push_event(...)
end
</code></pre>
<p>I only use the <code>handle_event</code> to destructure the socket, and all operations on socket is done in <code>do_something</code> call, which ofen contains long chain of piping function calls.</p>
<p>Disclaimer: I don’t think my idiom is the one true idiom that everyone else should follow.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="317829" 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/reply-function-for-liveviews/61534/16">Post #15</a>
	                </div>
	            </div>
              <div id="likers-container-317829" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="317829"
                     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="317832" data-post-id="317832">
  <section>
    <div class="post-wrap">


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

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>I agree and did something similar to make the code easier to read IMO. Some might have objections because it adds special conventions to the code base but it works well for me. Especially the <code>noreply/1</code> which is super simple.</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">def ok(value, extra \\ nil)
def ok(value, nil), do: {:ok, value}
def ok(value, extra), do: {:ok, value, extra}
def error(reason), do: {:error, reason}
def reply(state, res), do: {:reply, res, state}
def noreply(state), do: {:noreply, state}
</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="317832" 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/reply-function-for-liveviews/61534/17">Post #16</a>
	                </div>
	            </div>
              <div id="likers-container-317832" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="317832"
                     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="317835" data-post-id="317835">
  <section>
    <div class="post-wrap">


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

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="Aduril" data-post="11" data-topic="61534">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/aduril/48/18040_2.png" class="avatar"> Aduril:</div>
<blockquote>
<p>The paradigm of LiveView encourages exactly that pattern with a socket being transformed into another socket. This pattern breaks only at the end.</p>
</blockquote>
</aside>
<p>I believe this is the wrong way to think about it.  We’re certainly transforming a socket, but what we’re returning is a different value altogether that contains the socket but also additional information that is used for branching.  If we really wanted to make it about piping a socket all the way through, that additional info would need to be part of the socket, ie, have a <code>socket.reply</code> key or something like that.  But of course I don’t think it really belongs there, so we need to return a wrapper value.  If Elixir had built-in monads then we could do something closer to what you’re talking about where we shove the wrapped monadic value all the way through from the get-go, but introducing a pipeable function is just masking what is going on and not actually solving what you’re talking about.  All it does is give us a visual pipeline.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="317835" 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/reply-function-for-liveviews/61534/18">Post #17</a>
	                </div>
	            </div>
              <div id="likers-container-317835" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="317835"
                     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="317883" data-post-id="317883">
  <section>
    <div class="post-wrap">


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

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Yes, IMO this is the right way to see it in the context of a <code>LiveView</code> pipeline. We’re transforming the <code>socket</code> except in the last step when we’re returing a tuple. And as such we should not hide this in a opaque pipe step. But I like the shortcut <code>|&gt; noreply()</code> too much because it is nice visually <img src="https://forum.elixirforum.com/images/emoji/apple/blush.png?v=15" title=":blush:" class="emoji" alt=":blush:" loading="lazy" width="20" height="20"><br>
More generally, a pipeline step transforms A into B which could be of a different type from A. Since the use of <code>|&gt; noreply()</code> is the last step of the pipeline, it is more forgivable, even if it breaks the <code>LiveView</code> convention.<br>
I just wanted to discuss the matter a little bit but I do not agree with incorporating that idea in Phoenix given the reasons you exposed.<br>
As a side note, I’ve also used basic monadic code based on the idea of <a href="https://fsharpforfunandprofit.com/posts/recipe-part2/" rel="noopener nofollow ugc">Railway Oriented Programming</a> by Scott Wlaschin and a post on Medium. Stuff like:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmacro left &gt;&gt;&gt; right do
  quote do
    (fn -&gt;
      case unquote(left) do
        {:ok, x}           -&gt; x |&gt; unquote(right)
        {:error, _} = expr -&gt; expr
      end
    end).()
  end
end
</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="317883" 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/reply-function-for-liveviews/61534/19">Post #18</a>
	                </div>
	            </div>
              <div id="likers-container-317883" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="317883"
                     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="317887" data-post-id="317887">
  <section>
    <div class="post-wrap">


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

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>That monad thing is neat, though I personally don’t actually care that Elixir doesn’t have codified monads and kind of like that it doesn’t.  Elixir hits this really nice mix of elegant and scrappy for me that not only makes it productive but also less gatekeep-y.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="317887" 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/reply-function-for-liveviews/61534/20">Post #19</a>
	                </div>
	            </div>
              <div id="likers-container-317887" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="317887"
                     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>