<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body dir="auto"><div dir="ltr"></div><div dir="ltr">The idiom I use for non-terminating processes with async looks like this (snippet from the end of a main function):</div><div dir="ltr"><br></div><div dir="ltr"><table class="highlight tab-size js-file-line-container js-code-nav-container js-tagsearch-file" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-lang="Haskell" data-tagsearch-path="home/src/Lib.hs" style="box-sizing: border-box; border-spacing: 0px; border-collapse: collapse; tab-size: 8; caret-color: rgb(5, 12, 20); color: rgb(5, 12, 20); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 14px; -webkit-text-size-adjust: 100%;"><tbody style="box-sizing: border-box;"><tr style="box-sizing: border-box;"><td id="LC1575" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; color: var(--color-fg-default); word-wrap: normal; white-space: pre;"><br></td></tr><tr style="box-sizing: border-box;"><td id="L1576" class="blob-num js-line-number js-code-nav-line-number js-blob-rnum" data-line-number="1576" style="box-sizing: border-box; padding: 0px 10px; position: relative; width: 50px; min-width: 50px; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; line-height: 20px; color: var(--color-fg-subtle); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; -webkit-user-select: none;"></td><td id="LC1576" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; color: var(--color-fg-default); word-wrap: normal; white-space: pre;">  outputter <span class="pl-k" style="box-sizing: border-box; color: var(--color-prettylights-syntax-keyword);"><-</span> async <span class="pl-k" style="box-sizing: border-box; color: var(--color-prettylights-syntax-keyword);">$</span></td></tr><tr style="box-sizing: border-box;"><td id="L1577" class="blob-num js-line-number js-code-nav-line-number js-blob-rnum" data-line-number="1577" style="box-sizing: border-box; padding: 0px 10px; position: relative; width: 50px; min-width: 50px; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; line-height: 20px; color: var(--color-fg-subtle); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; -webkit-user-select: none;"></td><td id="LC1577" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; color: var(--color-fg-default); word-wrap: normal; white-space: pre;">    logged <span class="pl-k" style="box-sizing: border-box; color: var(--color-prettylights-syntax-keyword);">$</span></td></tr><tr style="box-sizing: border-box;"><td id="L1578" class="blob-num js-line-number js-code-nav-line-number js-blob-rnum" data-line-number="1578" style="box-sizing: border-box; padding: 0px 10px; position: relative; width: 50px; min-width: 50px; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; line-height: 20px; color: var(--color-fg-subtle); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; -webkit-user-select: none;"></td><td id="LC1578" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; color: var(--color-fg-default); word-wrap: normal; white-space: pre;">      forever <span class="pl-k" style="box-sizing: border-box; color: var(--color-prettylights-syntax-keyword);">$</span> <span class="pl-k" style="box-sizing: border-box; color: var(--color-prettylights-syntax-keyword);">do</span></td></tr><tr style="box-sizing: border-box;"><td id="L1579" class="blob-num js-line-number js-code-nav-line-number js-blob-rnum" data-line-number="1579" style="box-sizing: border-box; padding: 0px 10px; position: relative; width: 50px; min-width: 50px; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; line-height: 20px; color: var(--color-fg-subtle); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; -webkit-user-select: none;"></td><td id="LC1579" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; color: var(--color-fg-default); word-wrap: normal; white-space: pre;">        (topic, msg) <span class="pl-k" style="box-sizing: border-box; color: var(--color-prettylights-syntax-keyword);"><-</span> liftIO <span class="pl-k" style="box-sizing: border-box; color: var(--color-prettylights-syntax-keyword);">$</span> readChan outputChan</td></tr><tr style="box-sizing: border-box;"><td id="L1580" class="blob-num js-line-number js-code-nav-line-number js-blob-rnum" data-line-number="1580" style="box-sizing: border-box; padding: 0px 10px; position: relative; width: 50px; min-width: 50px; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; line-height: 20px; color: var(--color-fg-subtle); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; -webkit-user-select: none;"></td><td id="LC1580" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; color: var(--color-fg-default); word-wrap: normal; white-space: pre;">        liftIO <span class="pl-k" style="box-sizing: border-box; color: var(--color-prettylights-syntax-keyword);">$</span> <span class="pl-c1" style="box-sizing: border-box; color: var(--color-prettylights-syntax-constant);">MC.</span>publish client topic msg <span class="pl-ent" style="box-sizing: border-box; color: var(--color-prettylights-syntax-entity-tag);">False</span></td></tr><tr style="box-sizing: border-box;"><td id="L1581" class="blob-num js-line-number js-code-nav-line-number js-blob-rnum" data-line-number="1581" style="box-sizing: border-box; padding: 0px 10px; position: relative; width: 50px; min-width: 50px; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; line-height: 20px; color: var(--color-fg-subtle); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; -webkit-user-select: none;"></td><td id="LC1581" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; color: var(--color-fg-default); word-wrap: normal; white-space: pre;">  automation <span class="pl-k" style="box-sizing: border-box; color: var(--color-prettylights-syntax-keyword);"><-</span> async <span class="pl-k" style="box-sizing: border-box; color: var(--color-prettylights-syntax-keyword);">$</span> logged <span class="pl-k" style="box-sizing: border-box; color: var(--color-prettylights-syntax-keyword);">$</span> liftIO <span class="pl-k" style="box-sizing: border-box; color: var(--color-prettylights-syntax-keyword);">$</span> <span class="pl-c1" style="box-sizing: border-box; color: var(--color-prettylights-syntax-constant);">MC.</span>waitForClient client</td></tr><tr style="box-sizing: border-box;"><td id="L1582" class="blob-num js-line-number js-code-nav-line-number js-blob-rnum" data-line-number="1582" style="box-sizing: border-box; padding: 0px 10px; position: relative; width: 50px; min-width: 50px; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; line-height: 20px; color: var(--color-fg-subtle); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; -webkit-user-select: none;"></td><td id="LC1582" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; color: var(--color-fg-default); word-wrap: normal; white-space: pre;">  calendarChecker <span class="pl-k" style="box-sizing: border-box; color: var(--color-prettylights-syntax-keyword);"><-</span> async <span class="pl-k" style="box-sizing: border-box; color: var(--color-prettylights-syntax-keyword);">$</span> logged <span class="pl-k" style="box-sizing: border-box; color: var(--color-prettylights-syntax-keyword);">$</span> handleCalendarEvents (liftIO <span class="pl-k" style="box-sizing: border-box; color: var(--color-prettylights-syntax-keyword);">.</span> calendar)</td></tr><tr style="box-sizing: border-box;"><td id="L1583" class="blob-num js-line-number js-code-nav-line-number js-blob-rnum" data-line-number="1583" style="box-sizing: border-box; padding: 0px 10px; position: relative; width: 50px; min-width: 50px; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; line-height: 20px; color: var(--color-fg-subtle); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; -webkit-user-select: none;"></td><td id="LC1583" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; color: var(--color-fg-default); word-wrap: normal; white-space: pre;">  (_, err <span class="pl-k" style="box-sizing: border-box; color: var(--color-prettylights-syntax-keyword);">::</span> <span class="pl-en" style="box-sizing: border-box; color: var(--color-prettylights-syntax-entity);">String</span>) <span class="pl-k" style="box-sizing: border-box; color: var(--color-prettylights-syntax-keyword);"><-</span></td></tr><tr style="box-sizing: border-box;"><td id="L1584" class="blob-num js-line-number js-code-nav-line-number js-blob-rnum" data-line-number="1584" style="box-sizing: border-box; padding: 0px 10px; position: relative; width: 50px; min-width: 50px; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; line-height: 20px; color: var(--color-fg-subtle); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; -webkit-user-select: none;"></td><td id="LC1584" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; color: var(--color-fg-default); word-wrap: normal; white-space: pre;">    liftIO <span class="pl-k" style="box-sizing: border-box; color: var(--color-prettylights-syntax-keyword);">$</span></td></tr><tr style="box-sizing: border-box;"><td id="L1585" class="blob-num js-line-number js-code-nav-line-number js-blob-rnum" data-line-number="1585" style="box-sizing: border-box; padding: 0px 10px; position: relative; width: 50px; min-width: 50px; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; line-height: 20px; color: var(--color-fg-subtle); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; -webkit-user-select: none;"></td><td id="LC1585" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; color: var(--color-fg-default); word-wrap: normal; white-space: pre;">      waitAny</td></tr><tr style="box-sizing: border-box;"><td id="L1586" class="blob-num js-line-number js-code-nav-line-number js-blob-rnum" data-line-number="1586" style="box-sizing: border-box; padding: 0px 10px; position: relative; width: 50px; min-width: 50px; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; line-height: 20px; color: var(--color-fg-subtle); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; -webkit-user-select: none;"></td><td id="LC1586" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; color: var(--color-fg-default); word-wrap: normal; white-space: pre;">        [ <span class="pl-s" style="box-sizing: border-box; color: var(--color-prettylights-syntax-string);"><span class="pl-pds" style="box-sizing: border-box; color: var(--color-prettylights-syntax-string);">"</span>logger1 died<span class="pl-pds" style="box-sizing: border-box; color: var(--color-prettylights-syntax-string);">"</span></span> <span class="pl-k" style="box-sizing: border-box; color: var(--color-prettylights-syntax-keyword);"><$</span> logger1,</td></tr><tr style="box-sizing: border-box;"><td id="L1587" class="blob-num js-line-number js-code-nav-line-number js-blob-rnum" data-line-number="1587" style="box-sizing: border-box; padding: 0px 10px; position: relative; width: 50px; min-width: 50px; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; line-height: 20px; color: var(--color-fg-subtle); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; -webkit-user-select: none;"></td><td id="LC1587" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; color: var(--color-fg-default); word-wrap: normal; white-space: pre;">          <span class="pl-s" style="box-sizing: border-box; color: var(--color-prettylights-syntax-string);"><span class="pl-pds" style="box-sizing: border-box; color: var(--color-prettylights-syntax-string);">"</span>logger2 died<span class="pl-pds" style="box-sizing: border-box; color: var(--color-prettylights-syntax-string);">"</span></span> <span class="pl-k" style="box-sizing: border-box; color: var(--color-prettylights-syntax-keyword);"><$</span> logger2,</td></tr><tr style="box-sizing: border-box;"><td id="L1588" class="blob-num js-line-number js-code-nav-line-number js-blob-rnum" data-line-number="1588" style="box-sizing: border-box; padding: 0px 10px; position: relative; width: 50px; min-width: 50px; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; line-height: 20px; color: var(--color-fg-subtle); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; -webkit-user-select: none;"></td><td id="LC1588" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; color: var(--color-fg-default); word-wrap: normal; white-space: pre;">          <span class="pl-s" style="box-sizing: border-box; color: var(--color-prettylights-syntax-string);"><span class="pl-pds" style="box-sizing: border-box; color: var(--color-prettylights-syntax-string);">"</span>reporter died<span class="pl-pds" style="box-sizing: border-box; color: var(--color-prettylights-syntax-string);">"</span></span> <span class="pl-k" style="box-sizing: border-box; color: var(--color-prettylights-syntax-keyword);"><$</span> reporter,</td></tr><tr style="box-sizing: border-box;"><td id="L1589" class="blob-num js-line-number js-code-nav-line-number js-blob-rnum" data-line-number="1589" style="box-sizing: border-box; padding: 0px 10px; position: relative; width: 50px; min-width: 50px; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; line-height: 20px; color: var(--color-fg-subtle); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; -webkit-user-select: none;"></td><td id="LC1589" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; color: var(--color-fg-default); word-wrap: normal; white-space: pre;">          <span class="pl-s" style="box-sizing: border-box; color: var(--color-prettylights-syntax-string);"><span class="pl-pds" style="box-sizing: border-box; color: var(--color-prettylights-syntax-string);">"</span>client died<span class="pl-pds" style="box-sizing: border-box; color: var(--color-prettylights-syntax-string);">"</span></span> <span class="pl-k" style="box-sizing: border-box; color: var(--color-prettylights-syntax-keyword);"><$</span> automation,</td></tr><tr style="box-sizing: border-box;"><td id="L1590" class="blob-num js-line-number js-code-nav-line-number js-blob-rnum" data-line-number="1590" style="box-sizing: border-box; padding: 0px 10px; position: relative; width: 50px; min-width: 50px; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; line-height: 20px; color: var(--color-fg-subtle); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; -webkit-user-select: none;"></td><td id="LC1590" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; color: var(--color-fg-default); word-wrap: normal; white-space: pre;">          <span class="pl-s" style="box-sizing: border-box; color: var(--color-prettylights-syntax-string);"><span class="pl-pds" style="box-sizing: border-box; color: var(--color-prettylights-syntax-string);">"</span>outputter died<span class="pl-pds" style="box-sizing: border-box; color: var(--color-prettylights-syntax-string);">"</span></span> <span class="pl-k" style="box-sizing: border-box; color: var(--color-prettylights-syntax-keyword);"><$</span> outputter,</td></tr><tr style="box-sizing: border-box;"><td id="L1591" class="blob-num js-line-number js-code-nav-line-number js-blob-rnum" data-line-number="1591" style="box-sizing: border-box; padding: 0px 10px; position: relative; width: 50px; min-width: 50px; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; line-height: 20px; color: var(--color-fg-subtle); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; -webkit-user-select: none;"></td><td id="LC1591" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; color: var(--color-fg-default); word-wrap: normal; white-space: pre;">          <span class="pl-s" style="box-sizing: border-box; color: var(--color-prettylights-syntax-string);"><span class="pl-pds" style="box-sizing: border-box; color: var(--color-prettylights-syntax-string);">"</span>calendar died<span class="pl-pds" style="box-sizing: border-box; color: var(--color-prettylights-syntax-string);">"</span></span> <span class="pl-k" style="box-sizing: border-box; color: var(--color-prettylights-syntax-keyword);"><$</span> calendarChecker</td></tr><tr style="box-sizing: border-box;"><td id="L1592" class="blob-num js-line-number js-code-nav-line-number js-blob-rnum" data-line-number="1592" style="box-sizing: border-box; padding: 0px 10px; position: relative; width: 50px; min-width: 50px; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; line-height: 20px; color: var(--color-fg-subtle); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; -webkit-user-select: none;"></td><td id="LC1592" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; color: var(--color-fg-default); word-wrap: normal; white-space: pre;">        ]</td></tr><tr style="box-sizing: border-box;"><td id="L1593" class="blob-num js-line-number js-code-nav-line-number js-blob-rnum" data-line-number="1593" style="box-sizing: border-box; padding: 0px 10px; position: relative; width: 50px; min-width: 50px; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; line-height: 20px; color: var(--color-fg-subtle); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; -webkit-user-select: none;"></td><td id="LC1593" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; color: var(--color-fg-default); word-wrap: normal; white-space: pre;">  <span class="pl-c1" style="box-sizing: border-box; color: var(--color-prettylights-syntax-constant);">print</span> err</td></tr></tbody></table></div><div dir="ltr"><br></div><div dir="ltr">Typically these things like `automation` will return `IO void`, but they can return whatever you like. </div><div dir="ltr"><br></div><div dir="ltr"><br></div><div dir="ltr">    </div><div dir="ltr"><br><blockquote type="cite">On Mar 9, 2022, at 08:10, Olaf Klinke <olf@aatal-apotheke.de> wrote:<br><br></blockquote></div><blockquote type="cite"><div dir="ltr"><span>On Mon, 2022-03-07 at 19:59 +0000, coot@coot.me wrote:</span><br><blockquote type="cite"><span>Hi Olaf,</span><br></blockquote><blockquote type="cite"><span></span><br></blockquote><blockquote type="cite"><span>`forkIO` is rather a low level.  It's more common to use async package (https://hackage.haskell.org/package/async).  Async has `waitCatch` which allows you to wait for a thread to finish and get access to either an exception which killed the thread or the result of the thread handler.</span><br></blockquote><blockquote type="cite"><span></span><br></blockquote><blockquote type="cite"><span>Best regards,</span><br></blockquote><blockquote type="cite"><span>Marcin Szamotulski</span><br></blockquote><blockquote type="cite"><span></span><br></blockquote><blockquote type="cite"><span>Sent with ProtonMail secure email.</span><br></blockquote><span></span><br><span>Thanks for pointing this out, Marcin. </span><br><span>Async seems to offer much better abstractions than what GHC.Conc</span><br><span>provides for  ThreadId. </span><br><span>I have the impression, though, that Async was written for threads that</span><br><span>are supposed to do their work and eventually terminate. </span><br><span>In my application, a webserver forks several perpetually running</span><br><span>threads and offers supervision to the user. Therefore withAsync is not</span><br><span>perfectly suited, as we do not know upfront when and what we're going</span><br><span>to do with the Async handle. I resorted to the following pattern. </span><br><span></span><br><span>import Control.Concurrent.Async</span><br><span>import Control.Concurrent</span><br><span>import Control.Exception (SomeException)</span><br><span></span><br><span>type MyThread = (IO (),MVar (Async ()))</span><br><span></span><br><span>startThread :: MyThread -> IO ()</span><br><span>startThread (action,var) = withAsync action (putMVar var)</span><br><span></span><br><span>pauseThread :: MyThread -> IO ()</span><br><span>pauseThread (_,var) = do</span><br><span>    a <- takeMVar var</span><br><span>    cancel a</span><br><span></span><br><span>data MyThreadStatus = Paused | Running | Died SomeException</span><br><span>threadStatus :: MyThread -> IO MyThreadStatus</span><br><span>threadStatus (_,var) = do</span><br><span>    running <- tryReadMVar var</span><br><span>    case running of</span><br><span>        Nothing -> return Paused</span><br><span>        Just a -> do</span><br><span>            finished <- poll a</span><br><span>            case finished of</span><br><span>                Nothing         -> return Running</span><br><span>                Just (Right _)  -> return Paused</span><br><span>                Just (Left why) -> return (Died why)</span><br><span></span><br><span>-- Olaf</span><br><span></span><br><blockquote type="cite"><span></span><br></blockquote><blockquote type="cite"><span>------- Original Message -------</span><br></blockquote><blockquote type="cite"><span></span><br></blockquote><blockquote type="cite"><span>On Monday, March 7th, 2022 at 10:56, Olaf Klinke <olf@aatal-apotheke.de> wrote:</span><br></blockquote><blockquote type="cite"><span></span><br></blockquote><blockquote type="cite"><blockquote type="cite"><span>Dear Cafe,</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>I had expected to see ThreadDied in the small example below.</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>But when I compile with</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>ghc --make -threaded -with-rtsopts=-N2</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>The output is:</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>threadStatus: user error (child thread is crashing!)</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>The status of my child is:</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>ThreadFinished</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>The output is not really a lie. But how do I determine whether a child</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>thread has exited normally or not? Wouldn't you say a call to fail (or</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>any other throwIO) should count as ThreadDied?</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>The documentation of GHC.Conc.forkIO says:</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>"... passes all other exceptions to the uncaught exception handler."</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>and the documentation for GHC.Conc.ThreadStatus says:</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>ThreadDied -- the thread received an uncaught exception</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>One can provoke ThreadDied by using throwTo from the parent thread. So</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>the emphasis in the documentation of ThreadDied should be on the word</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>"received".</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>This is a case of misleading documentation, in my humble opinion.</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>The constructor should not be named ThreadDied because that suggests</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>inclusion of internal reasons.</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>Olaf</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>-- begin threadStatus.hs</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>import Control.Concurrent</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>import GHC.Conc</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>main = mainThread</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>childThread :: IO ()</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>childThread = fail "child thread is crashing!"</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>mainThread :: IO ()</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>mainThread = do</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>child <- forkIO childThread</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>threadDelay 5000</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>status <- threadStatus child</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>putStr "The status of my child is: "</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>print status</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>-- end threadStatus.hs</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>_______________________________________________</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>Haskell-Cafe mailing list</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>To (un)subscribe, modify options or view archives go to:</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>Only members subscribed via the mailman list are allowed to post.</span><br></blockquote></blockquote><span></span><br><span></span><br><span>_______________________________________________</span><br><span>Haskell-Cafe mailing list</span><br><span>To (un)subscribe, modify options or view archives go to:</span><br><span>http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe</span><br><span>Only members subscribed via the mailman list are allowed to post.</span></div></blockquote></body></html>