3 ms·
Good points! The time where the `while` loop is actually better is very situational, but when it is a better fit, it is definitely handy to have in your back po
by bitwalker 7y ago
Good points! The time where the `while` loop is actually better is very situational, but when it is a better fit, it is definitely handy to have in your back pocket. But more generally I just think it is useful to know how you can implement language constructs like this in Elixir with nothing more than the primitives it provides, some macro glue, and a bit of creativity :)
A couple things though; one issue with your first example is that you incur a 1s wait for either success or failure conditions, where what you typically want is for only the failure condition to impose that cost. Luckily, you can keep this approach and fix it by removing `Stream.zip(Stream.interval(1_000))` and replacing it with `Stream.intersperse(Stream.interval(1_000))`.
The other problem though is that you end up incurring quite a few additional function calls vs the `while`, since the `while` inlines the predicate and associated machinery. You end up with precisely one function call per iteration, with the ability to exit early on failure or timeout, whereas the function-based form is going to incur potentially several function calls, depending on how the `Stream` is being constructed - if I recall implementation details correctly, your first example would be a minimum of 3 calls per iteration; one for the unfold, one for the interval, and one for the take_while.
Of course, that's all really just optimization, and your `Stream` based solution is perfectly fine in general. The only additional benefit (IMO) to the `while` form, is that it reads very succinctly, and due to most being familiar with imperative constructs like that, they can read it with virtually no effort, where reading the `Stream` form takes some care to understand what you get at each step, and what it produces in the end.
RE: Mutability, I think I mentioned it in the post, but I really didn't want _actual_ mutability, just the appearance of it. So while one could use the process dictionary, or ETS, or even another process, to get something like "real" mutability, that kind of defeats the point ;)
- elcritch 7y agoIt was a good read! Especially since you went through all the details. Handling different inputs and alternates like `after` are a bit tricky. And you're right on the downsides of the "stream" style. There's a bit of overhead compared to the macro form which can inline things a bit. It also takes a couple of more "cognitive" cycles to write, but usually I can read the results more quickly later. In general I do wish Elixir provided a `reduce` macro similar to the for-loop one with the ability to halt. While-loop logic is easier to refashion as a reduce but it's harder to recall the argument orders for various Enum.reduce forms. Do you use this while-macro in production code or just test code? Yah the "real" mutability tricks are more for those wondering if you _can_ do it. Sometimes it's not obvious, but one of the nice things in BEAM is that you can cheat. P.S. thanks for all of your Elixir tools!
- bitwalker 7y ago> Do you use this while-macro in production code or just test code? I use the `while` macro in the Distillery tests, since it made expressing certain tests much less verbose - namely those dealing with building up a release, spinning it up in a new node, and then verifying conditions on the running node meet expectations. In general though, I don't. I haven't needed it elsewhere (well, I have one other library I have considered using it in, but haven't yet). I would be hesitant to introduce it without a clear need, just because it would likely catch other developers off guard unless clearly documented. To be clear though, I would support something like it in the language, but also understand why it isn't there; there just isn't a clear enough need, and as you've demonstrated, `Stream` can more or less be used to cover the general cases. > P.S. thanks for all of your Elixir tools! Thanks for the kind words!