{"id":108652,"date":"2023-08-25T07:00:00","date_gmt":"2023-08-25T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=108652"},"modified":"2023-08-25T06:47:51","modified_gmt":"2023-08-25T13:47:51","slug":"20230825-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230825-00\/?p=108652","title":{"rendered":"On writing loops in continuation-passing style, part 4"},"content":{"rendered":"<p>So far, we&#8217;ve been look at <a title=\"On writing loops in PPL and continuation-passing style, part 3\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230824-00\/?p=108647\"> writing loops in PPL and continuation-passing style<\/a>, and a lot of the complications came from creating <code>shared_ptr<\/code>s to manage shared state without copying, and trying to reduce the number of such pointers we had to make. The equivalent helper functions in C# and JavaScript are simpler because in those languages, references act like <code>shared_ptr<\/code> already; there&#8217;s no need to convert them into shared pointers explicitly.<\/p>\n<pre>class TaskHelpers\r\n{\r\n    public static Task DoWhileTask(Func&lt;Task&lt;bool&gt;&gt; callable)\r\n    {\r\n        return callable().ContinueWith(t =&gt;\r\n            t.Result ? DoWhileTask(callable)\r\n                     : Task.CompletedTask).Unwrap();\r\n    }\r\n}\r\n<\/pre>\n<p>The C# Task Parallel Library&#8217;s <code>ContinueWith<\/code> method is the equivalent to the PPL <code>then()<\/code> method: You give it a <code>Func&lt;Task&lt;T&gt;, Result&gt;<\/code> which is called with the preceding task. In our case, we are given a <code>Task&lt;bool&gt;<\/code>: We check the result, and if it is <code>true<\/code>, then we recurse back and do the whole thing again.<\/p>\n<p>The gotcha is that <code>ContinueWith<\/code> returns a task whose result type matches the return value of the <code>Func<\/code> you passed in. In our case, that <code>Func<\/code> returns a <code>Task<\/code>, so the return value of <code>ContinueWith<\/code> is a rather confusing <code>Task&lt;Task&gt;<\/code>. You need to follow up with the <code>Unwrap()<\/code> method to unwrap one layer and get a <code>Task<\/code> back. (More generally, the <code>Unwrap<\/code> method converts a <code>Task&lt;Task&lt;T&gt;&gt;<\/code> to a <code>Task&lt;T&gt;<\/code>.)<\/p>\n<p>The JavaScript version is comparable.<\/p>\n<pre>function do_while_task(callable) {\r\n    return callable().then(loop =&gt;\r\n        loop ? do_while_task(callable) : undefined);\r\n}\r\n<\/pre>\n<p>We take advantage of the JavaScript convenience that the continuation function can return either a Promise or a value, so instead of returning a settled Promise, we just return <code>undefined<\/code> and let that be the result of the promise chain.<\/p>\n<p>We can code golf it a little more by using the <code>&amp;&amp;<\/code> operator:<\/p>\n<pre>function do_while_task(callable) {\r\n    return callable().then(loop =&gt;\r\n        loop &amp;&amp; do_while_task(callable));\r\n}\r\n<\/pre>\n<p>In time, C++, C#, and JavaScript all gained some variation of the <code>await<\/code> keyword, and it&#8217;s probably easier to use that keyword if you can.<\/p>\n<pre>\/\/ C++\/PPL\r\ntask&lt;void&gt; create_many_widgets(Widget* widgets, int count)\r\n{\r\n    for (int i = 0; i &lt; count; i++) {\r\n        widgets[0] = co_await create_widget();\r\n    }\r\n}\r\n\r\n\/\/ C#\r\nasync Task CreateManyWidgets(Widget[] widgets)\r\n{\r\n    for (int i = 0; i &lt; widgets.Count; i++) {\r\n        widgets[i] = await CreateWidget();\r\n    }\r\n}\r\n\r\n\/\/ JavaScript\r\nasync function createManyWidgets(widgets) {\r\n    for (var i = 0; i &lt; widgets.length; i++) {\r\n        widgets[i] = await createWidget();\r\n    }\r\n}\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Equivalents in C# and JavaScript.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-108652","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Equivalents in C# and JavaScript.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108652","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=108652"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108652\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=108652"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=108652"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=108652"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}