<div dir="ltr">Fred & <span style="color:rgb(51,51,51);font-family:'normal arial',sans-serif;line-height:20px">ok</span><span style="color:rgb(51,51,51);font-family:'normal arial',sans-serif;line-height:20px">@<a href="http://cs.otago.ac.nz" target="_blank">cs.otago.ac.nz</a></span> (not sure what else to call you :>), thanks for your thoughtful answers. They're helping to provide the insight I'm looking for. More below...<div class="gmail_extra"><br><div class="gmail_quote">On Mon, Mar 30, 2015 at 7:39 AM, Fred Hebert <span dir="ltr"><<a href="mailto:mononcqc@ferd.ca" target="_blank">mononcqc@ferd.ca</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><span>On 03/29, Youngkin, Rich wrote:<br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">
Fred's response in [2] is interesting and informative, but it misses the mark in that it's up to the developer to know/understand how and when to implement the appropriate pattern. That's what capabilities such as gen_server are intended to eliminate.<br>
</blockquote>
<br></span>
I'm not sure I understand the criticism there. [2] was about using RPC/futures, and the answer shows how to use RPC/futures.<br></blockquote><div><br></div><div>Maybe I'm splitting hairs here, but my point is that an RPC isn't a Future (even though the Erlang documentation on rpc:async_call/4 states that it implements "call streams with promises"). Futures can be implemented in Erlang using RPCs, but, to me, they're not equivalent semantic concepts. My intent wasn't to criticise your answer. Your answer does clearly show how to implement futures using RPCs and I found it extremely helpful.</div><div><br></div><div>Garrett Smith gave a great presentation at last week's Erlang conference about "The Timeless Way of Building Erlang Apps" (see [3] and <a href="http://erlangpatterns.org" target="_blank">erlangpatterns.org</a>). What I think could be useful is a "pattern" description for futures that incorporates the implementation using RPCs. I may just do this if someone doesn't beat me to it.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><span><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><br>
The example above is interesting from a semantic perspective, but it's<br>
mixing the happy path with failure handling. This is where the next<br>
concept/capability of Akka is interesting, namely the ability to compose<br>
operations while separating the failure handling path from the "happy<br>
path". Here's the follow-on example from the same course:<br>
<br>
</blockquote>
<br></span>
This is where a difference is made with these types/monads, yes!<span><br>
<br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">
1 val treasure: Try[Treasure] =<br>
2 adventure.collectCoins().<u></u>flatMap(coins => {<br>
3 adventure.buyTreasure(coins)<br>
4 })<br>
5<br>
6 treasure match {<br>
7 case Success(successValue) =><br>
9 do something like continue to the next challenge...<br>
10 case Failure(errorValue) =><br>
11 do something like make the character repeat the previous challenge...<br>
<br>
So the "happy path" of collectCoins() and buyTreasure() isn't intermingled<br>
with what to do if one or both of these operations fail. Specifically,<br>
buyTreasure() won't throw an exception if collectCoins() fails. I don't<br>
know of any way to express this in Erlang.<br>
</blockquote>
<br></span>
Yes. So the regular `try .. catch` attempt would be:<br>
<br>
try<br>
{ok, Cs} = adventure:collect_coins(),<br>
Res = lists:flatmap(fun(Coins) -><br>
{ok, Val} = adventure:buy_treasure(Coins),<br>
Val<br>
end, Cs),<br>
of<br>
SuccessValue -><br>
%% Do something like continue to next challenge<br>
catch<br>
Type:Reason -><br>
%% Do something like maybe repeat the previous challenge<br>
end.<br>
<br>
Given what we care about here is whether we actually failed *anywhere* or *nowhere* (at least based on your failure value match), this is equivalent to what you have. Interestingly, because you could be expected to see each operation fail, you might also have a callee go for harder failures:<br>
<br>
try<br>
lists:flatmap(fun(Coins) -> adventure:buy_treasure(Coins) end,<br>
adventure:collect_coins())<br>
of<br>
SuccessValue -> % Keep going<br>
catch<br>
_:_ -> % alt path<br>
end<br>
<br>
The distinction is there, and really, the challenge is picking which one to implement when you design the 'adventure' module. I'd argue for the former if you expect multiple operations to 'fail' (it is likely that a character cannot connect coins without it being a programmer error), so that the caller can choose how to handle alternative branches at every level.<br></blockquote><div><br></div><div>I don't see a difference between the previous 2 Erlang implementations. Can you elaborate?</div><div><br></div><div><br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><span><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><br>
1. Are these concepts generally useful or just interesting from an academic<br>
perspective?<br>
</blockquote>
<br></span>
They are truly useful in my opinion, but how needed they are may depend on what exception handling mechanism you have. Erlang does tend to have that pattern made explicit with sequences of case expression (as in my last example). Whether this is boilerplate that ought to be eliminated is likely a question of personal preferences.<span><br>
<br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">
2. Would it be useful to support these capabilities as first-class concepts </blockquote></span></blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><span><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">
in Erlang (similar to gen_servers)? Or is this so trivial in Erlang that<br>
it's not worth making these first class capabilities?<br>
</blockquote>
<br></span>
This is a more interesting question, because Erlang does have a lot of ways to handle exceptions. I mean you can have actual exceptions, option types, tagged values, multiple return values, signals, continuations, mixes of them, and so on.<br>
<br>...</blockquote><div><br></div><div>Yes, I do see why you wouldn't want to limit Erlang's capabilities in this area, especially if it results in losing required contextual data regarding the failure.</div><div><br></div><div>I also had a 3rd question regarding futures. Here it is from <span style="color:rgb(51,51,51);font-family:'normal arial',sans-serif;line-height:20px">ok</span><span style="color:rgb(51,51,51);font-family:'normal arial',sans-serif;line-height:20px">@<a href="http://cs.otago.ac.nz/" target="_blank">cs.otago.ac.nz</a></span>'s response:</div><div><br></div><div><span style="font-size:12.8000001907349px">>> 3. Is there any way to express these capabilities in Erlang (in addition<br>>> to<br>>> the rpc:async_call as described by Fred in [2], which only covers Futures,<br>>> and doesn't support composition)?<br><br></span><span style="font-size:12.8000001907349px">> You've now changed the subject from Try to Futures.</span><br style="font-size:12.8000001907349px"><span style="font-size:12.8000001907349px">> I think you may have misunderstood</span><br style="font-size:12.8000001907349px"><span style="font-size:12.8000001907349px">> [2] </span><a href="http://erlang.org/pipermail/erlang-questions/2012-November/070679.html" style="font-size:12.8000001907349px" target="_blank">http://erlang.org/pipermail/erlang-questions/2012-November/070679.html</a><br style="font-size:12.8000001907349px"><br style="font-size:12.8000001907349px"></div><div>Yes, I did change the subject to Futures rather abruptly. Sorry about that. It's possible that I misunderstood [2] above, but I don't think so. So to press on this a little more, is it possible to compose futures in Erlang in a manner similar to *Try* as shown above? I think the answer is yes, I think it's just a matter of chaining together rpc:async calls passing along the returned "Key" to the remaining calls to rpc:async_call, e.g., </div><div><br></div><div><div>27> Fn = fun(Key) -> rpc:nb_yield(Key, 45000), timer:sleep(10), 42 end.</div><div>#Fun<erl_eval.6.90072148></div><div>28> Key1 = rpc:async_call(node(), timer, sleep, [30000]).</div><div><0.74.0></div><div>29> Key2 = rpc:async_call(node(), erlang, apply, [Fn,Key1]).</div><div><0.76.0></div><div>30> rpc:nb_yield(Key2, 60000).</div></div><div><br></div><div>Note, this example fails with a badrpc on the final line (30), but I'm guessing this can be worked out.</div><div><br></div><div><br></div><div>To wrap up, Try, Future, Observable are useful concepts, perhaps worth expressing in a pattern someplace such as <a href="http://erlangpatterns.org" target="_blank">erlangpatterns.org</a>. I don't think this has to take away from the ability to use "actual exceptions, option types, tagged values...." if they are more appropriate to the problem at hand and are needed retain needed contextual data about a failure.</div><div><br></div><div>*Try* is a semantic that may be best expressed in Erlang as something like *-type try() :: any() | error*, and perhaps only in the context of a module. This example is a bit rough around the edges, but it's hopefully good enough to get the point across for now. Composing *Try* may be as simple as a try/catch block depending on the level of granularity that is needed and assuming that "let it crash" isn't appropriate.</div><div><br></div><div>*Future* is a pattern that can be implemented using rpc:async. I think this is worth documenting as a pattern. </div><div><br></div><div>*Observable" is something I didn't cover very well except to say that it's is future applied to a stream. This is a bit simplistic. An *Observable* is something that may produce events. To receive events, a subscription must be created by an *Observer*. After a subscription is created the *Observer* just listens for events. I can see how this can be easily accomplished in Erlang by registering an *Observer* (e.g., register self()) with an *Observable*) and then processing events as they arrive in the Observer's mailbox. In fact, this sounds a lot like gen_event.</div><div><br></div><div>Thanks again for the comments. Additional comments are welcome.<br></div><div><br></div><div>Cheers,</div><div>Rich</div><div><br></div><div>[3] <a href="https://www.youtube.com/watch?v=UUvU8cjCIcs&list=PLWbHc_FXPo2h0sJW6X2RZDtT1ndw6KKpQ&index=22">https://www.youtube.com/watch?v=UUvU8cjCIcs&list=PLWbHc_FXPo2h0sJW6X2RZDtT1ndw6KKpQ&index=22</a></div></div><br></div></div>