<!DOCTYPE html><html><head><title></title><style type="text/css">p.MsoNormal,p.MsoNoSpacing{margin:0}</style></head><body><div>Another good option for eternally long running things is to use <span class="font" style="font-family:menlo, consolas, monospace, sans-serif;">race</span>. I actually think <span class="font" style="font-family:menlo, consolas, monospace, sans-serif;">race</span> and <span class="font" style="font-family:menlo, consolas, monospace, sans-serif;">concurrently</span> are the much better go-to functions from the <span class="font" style="font-family:menlo, consolas, monospace, sans-serif;">async</span> library. I will often write code like:<br></div><div><br></div><div><span class="font" style="font-family:menlo, consolas, monospace, sans-serif;">race_ daemonThread mainThread >>= \case</span><span class="font" style="font-family:menlo, consolas, monospace, sans-serif;"><br></span></div><div><span class="font" style="font-family:menlo, consolas, monospace, sans-serif;">  Left _ -> fail "Daemon terminated"</span><span class="font" style="font-family:menlo, consolas, monospace, sans-serif;"><br></span></div><div><span class="font" style="font-family:menlo, consolas, monospace, sans-serif;">  Right a -> return a</span><br></div><div><br></div><div>This forks a thread for a daemon, a thread for the main thread, and then waits for either of them to finish. Assuming the daemon should never terminate, if it terminates the main thread will be terminated and the calling thread (e.g., main) will crash. If the other thread terminates early, that's fine, and we assume that's just the program gracefully terminating. In this case, the daemon will be cancelled.<br></div><div><br></div><div>Ollie<br></div><div><br></div><div>On Wed, 9 Mar 2022, at 3:39 PM, Will Yager wrote:<br></div><blockquote type="cite" id="qt" style=""><div dir="ltr"><br></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):<br></div><div dir="ltr"><br></div><div dir="ltr"><table class="qt-highlight qt-tab-size qt-js-file-line-container qt-js-code-nav-container qt-js-tagsearch-file" style="box-sizing:border-box;border-spacing:0px;border-collapse:collapse;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;"><tbody style="box-sizing:border-box;"><tr style="box-sizing:border-box;"><td id="qt-LC1575" class="qt-blob-code qt-blob-code-inner qt-js-file-line" style="box-sizing:border-box;padding-top:0px;padding-right:10px;padding-bottom:0px;padding-left:10px;position:relative;line-height:20px;vertical-align:top;overflow-x:visible;overflow-y:visible;font-family:ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;font-size:12px;color:var(--color-fg-default);overflow-wrap:normal;white-space:pre;"><br></td></tr><tr style="box-sizing:border-box;"><td id="qt-L1576" class="qt-blob-num qt-js-line-number qt-js-code-nav-line-number qt-js-blob-rnum" style="box-sizing:border-box;padding-top:0px;padding-right:10px;padding-bottom:0px;padding-left: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;"><br></td><td id="qt-LC1576" class="qt-blob-code qt-blob-code-inner qt-js-file-line" style="box-sizing:border-box;padding-top:0px;padding-right:10px;padding-bottom:0px;padding-left:10px;position:relative;line-height:20px;vertical-align:top;overflow-x:visible;overflow-y:visible;font-family:ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;font-size:12px;color:var(--color-fg-default);overflow-wrap:normal;white-space:pre;">outputter <span class="qt-pl-k" style="box-sizing:border-box;color:var(--color-prettylights-syntax-keyword);"><-</span> async <span class="qt-pl-k" style="box-sizing:border-box;color:var(--color-prettylights-syntax-keyword);">$</span><br></td></tr><tr style="box-sizing:border-box;"><td id="qt-L1577" class="qt-blob-num qt-js-line-number qt-js-code-nav-line-number qt-js-blob-rnum" style="box-sizing:border-box;padding-top:0px;padding-right:10px;padding-bottom:0px;padding-left: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;"><br></td><td id="qt-LC1577" class="qt-blob-code qt-blob-code-inner qt-js-file-line" style="box-sizing:border-box;padding-top:0px;padding-right:10px;padding-bottom:0px;padding-left:10px;position:relative;line-height:20px;vertical-align:top;overflow-x:visible;overflow-y:visible;font-family:ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;font-size:12px;color:var(--color-fg-default);overflow-wrap:normal;white-space:pre;">logged <span class="qt-pl-k" style="box-sizing:border-box;color:var(--color-prettylights-syntax-keyword);">$</span><br></td></tr><tr style="box-sizing:border-box;"><td id="qt-L1578" class="qt-blob-num qt-js-line-number qt-js-code-nav-line-number qt-js-blob-rnum" style="box-sizing:border-box;padding-top:0px;padding-right:10px;padding-bottom:0px;padding-left: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;"><br></td><td id="qt-LC1578" class="qt-blob-code qt-blob-code-inner qt-js-file-line" style="box-sizing:border-box;padding-top:0px;padding-right:10px;padding-bottom:0px;padding-left:10px;position:relative;line-height:20px;vertical-align:top;overflow-x:visible;overflow-y:visible;font-family:ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;font-size:12px;color:var(--color-fg-default);overflow-wrap:normal;white-space:pre;">forever <span class="qt-pl-k" style="box-sizing:border-box;color:var(--color-prettylights-syntax-keyword);">$</span> <span class="qt-pl-k" style="box-sizing:border-box;color:var(--color-prettylights-syntax-keyword);">do</span><br></td></tr><tr style="box-sizing:border-box;"><td id="qt-L1579" class="qt-blob-num qt-js-line-number qt-js-code-nav-line-number qt-js-blob-rnum" style="box-sizing:border-box;padding-top:0px;padding-right:10px;padding-bottom:0px;padding-left: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;"><br></td><td id="qt-LC1579" class="qt-blob-code qt-blob-code-inner qt-js-file-line" style="box-sizing:border-box;padding-top:0px;padding-right:10px;padding-bottom:0px;padding-left:10px;position:relative;line-height:20px;vertical-align:top;overflow-x:visible;overflow-y:visible;font-family:ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;font-size:12px;color:var(--color-fg-default);overflow-wrap:normal;white-space:pre;">(topic, msg) <span class="qt-pl-k" style="box-sizing:border-box;color:var(--color-prettylights-syntax-keyword);"><-</span> liftIO <span class="qt-pl-k" style="box-sizing:border-box;color:var(--color-prettylights-syntax-keyword);">$</span> readChan outputChan<br></td></tr><tr style="box-sizing:border-box;"><td id="qt-L1580" class="qt-blob-num qt-js-line-number qt-js-code-nav-line-number qt-js-blob-rnum" style="box-sizing:border-box;padding-top:0px;padding-right:10px;padding-bottom:0px;padding-left: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;"><br></td><td id="qt-LC1580" class="qt-blob-code qt-blob-code-inner qt-js-file-line" style="box-sizing:border-box;padding-top:0px;padding-right:10px;padding-bottom:0px;padding-left:10px;position:relative;line-height:20px;vertical-align:top;overflow-x:visible;overflow-y:visible;font-family:ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;font-size:12px;color:var(--color-fg-default);overflow-wrap:normal;white-space:pre;">liftIO <span class="qt-pl-k" style="box-sizing:border-box;color:var(--color-prettylights-syntax-keyword);">$</span> <span class="qt-pl-c1" style="box-sizing:border-box;color:var(--color-prettylights-syntax-constant);">MC.</span>publish client topic msg <span class="qt-pl-ent" style="box-sizing:border-box;color:var(--color-prettylights-syntax-entity-tag);">False</span><br></td></tr><tr style="box-sizing:border-box;"><td id="qt-L1581" class="qt-blob-num qt-js-line-number qt-js-code-nav-line-number qt-js-blob-rnum" style="box-sizing:border-box;padding-top:0px;padding-right:10px;padding-bottom:0px;padding-left: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;"><br></td><td id="qt-LC1581" class="qt-blob-code qt-blob-code-inner qt-js-file-line" style="box-sizing:border-box;padding-top:0px;padding-right:10px;padding-bottom:0px;padding-left:10px;position:relative;line-height:20px;vertical-align:top;overflow-x:visible;overflow-y:visible;font-family:ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;font-size:12px;color:var(--color-fg-default);overflow-wrap:normal;white-space:pre;">automation <span class="qt-pl-k" style="box-sizing:border-box;color:var(--color-prettylights-syntax-keyword);"><-</span> async <span class="qt-pl-k" style="box-sizing:border-box;color:var(--color-prettylights-syntax-keyword);">$</span> logged <span class="qt-pl-k" style="box-sizing:border-box;color:var(--color-prettylights-syntax-keyword);">$</span> liftIO <span class="qt-pl-k" style="box-sizing:border-box;color:var(--color-prettylights-syntax-keyword);">$</span> <span class="qt-pl-c1" style="box-sizing:border-box;color:var(--color-prettylights-syntax-constant);">MC.</span>waitForClient client<br></td></tr><tr style="box-sizing:border-box;"><td id="qt-L1582" class="qt-blob-num qt-js-line-number qt-js-code-nav-line-number qt-js-blob-rnum" style="box-sizing:border-box;padding-top:0px;padding-right:10px;padding-bottom:0px;padding-left: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;"><br></td><td id="qt-LC1582" class="qt-blob-code qt-blob-code-inner qt-js-file-line" style="box-sizing:border-box;padding-top:0px;padding-right:10px;padding-bottom:0px;padding-left:10px;position:relative;line-height:20px;vertical-align:top;overflow-x:visible;overflow-y:visible;font-family:ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;font-size:12px;color:var(--color-fg-default);overflow-wrap:normal;white-space:pre;">calendarChecker <span class="qt-pl-k" style="box-sizing:border-box;color:var(--color-prettylights-syntax-keyword);"><-</span> async <span class="qt-pl-k" style="box-sizing:border-box;color:var(--color-prettylights-syntax-keyword);">$</span> logged <span class="qt-pl-k" style="box-sizing:border-box;color:var(--color-prettylights-syntax-keyword);">$</span> handleCalendarEvents (liftIO <span class="qt-pl-k" style="box-sizing:border-box;color:var(--color-prettylights-syntax-keyword);">.</span> calendar)<br></td></tr><tr style="box-sizing:border-box;"><td id="qt-L1583" class="qt-blob-num qt-js-line-number qt-js-code-nav-line-number qt-js-blob-rnum" style="box-sizing:border-box;padding-top:0px;padding-right:10px;padding-bottom:0px;padding-left: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;"><br></td><td id="qt-LC1583" class="qt-blob-code qt-blob-code-inner qt-js-file-line" style="box-sizing:border-box;padding-top:0px;padding-right:10px;padding-bottom:0px;padding-left:10px;position:relative;line-height:20px;vertical-align:top;overflow-x:visible;overflow-y:visible;font-family:ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;font-size:12px;color:var(--color-fg-default);overflow-wrap:normal;white-space:pre;">(_, err <span class="qt-pl-k" style="box-sizing:border-box;color:var(--color-prettylights-syntax-keyword);">::</span> <span class="qt-pl-en" style="box-sizing:border-box;color:var(--color-prettylights-syntax-entity);">String</span>) <span class="qt-pl-k" style="box-sizing:border-box;color:var(--color-prettylights-syntax-keyword);"><-</span><br></td></tr><tr style="box-sizing:border-box;"><td id="qt-L1584" class="qt-blob-num qt-js-line-number qt-js-code-nav-line-number qt-js-blob-rnum" style="box-sizing:border-box;padding-top:0px;padding-right:10px;padding-bottom:0px;padding-left: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;"><br></td><td id="qt-LC1584" class="qt-blob-code qt-blob-code-inner qt-js-file-line" style="box-sizing:border-box;padding-top:0px;padding-right:10px;padding-bottom:0px;padding-left:10px;position:relative;line-height:20px;vertical-align:top;overflow-x:visible;overflow-y:visible;font-family:ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;font-size:12px;color:var(--color-fg-default);overflow-wrap:normal;white-space:pre;">liftIO <span class="qt-pl-k" style="box-sizing:border-box;color:var(--color-prettylights-syntax-keyword);">$</span><br></td></tr><tr style="box-sizing:border-box;"><td id="qt-L1585" class="qt-blob-num qt-js-line-number qt-js-code-nav-line-number qt-js-blob-rnum" style="box-sizing:border-box;padding-top:0px;padding-right:10px;padding-bottom:0px;padding-left: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;"><br></td><td id="qt-LC1585" class="qt-blob-code qt-blob-code-inner qt-js-file-line" style="box-sizing:border-box;padding-top:0px;padding-right:10px;padding-bottom:0px;padding-left:10px;position:relative;line-height:20px;vertical-align:top;overflow-x:visible;overflow-y:visible;font-family:ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;font-size:12px;color:var(--color-fg-default);overflow-wrap:normal;white-space:pre;">waitAny<br></td></tr><tr style="box-sizing:border-box;"><td id="qt-L1586" class="qt-blob-num qt-js-line-number qt-js-code-nav-line-number qt-js-blob-rnum" style="box-sizing:border-box;padding-top:0px;padding-right:10px;padding-bottom:0px;padding-left: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;"><br></td><td id="qt-LC1586" class="qt-blob-code qt-blob-code-inner qt-js-file-line" style="box-sizing:border-box;padding-top:0px;padding-right:10px;padding-bottom:0px;padding-left:10px;position:relative;line-height:20px;vertical-align:top;overflow-x:visible;overflow-y:visible;font-family:ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;font-size:12px;color:var(--color-fg-default);overflow-wrap:normal;white-space:pre;">[ <span class="qt-pl-s" style="box-sizing:border-box;color:var(--color-prettylights-syntax-string);"><span class="qt-pl-pds" style="box-sizing:border-box;color:var(--color-prettylights-syntax-string);">"</span>logger1 died<span class="qt-pl-pds" style="box-sizing:border-box;color:var(--color-prettylights-syntax-string);">"</span></span> <span class="qt-pl-k" style="box-sizing:border-box;color:var(--color-prettylights-syntax-keyword);"><$</span> logger1,<br></td></tr><tr style="box-sizing:border-box;"><td id="qt-L1587" class="qt-blob-num qt-js-line-number qt-js-code-nav-line-number qt-js-blob-rnum" style="box-sizing:border-box;padding-top:0px;padding-right:10px;padding-bottom:0px;padding-left: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;"><br></td><td id="qt-LC1587" class="qt-blob-code qt-blob-code-inner qt-js-file-line" style="box-sizing:border-box;padding-top:0px;padding-right:10px;padding-bottom:0px;padding-left:10px;position:relative;line-height:20px;vertical-align:top;overflow-x:visible;overflow-y:visible;font-family:ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;font-size:12px;color:var(--color-fg-default);overflow-wrap:normal;white-space:pre;"><span class="qt-pl-s" style="box-sizing:border-box;color:var(--color-prettylights-syntax-string);"><span class="qt-pl-pds" style="box-sizing:border-box;color:var(--color-prettylights-syntax-string);">"</span>logger2 died<span class="qt-pl-pds" style="box-sizing:border-box;color:var(--color-prettylights-syntax-string);">"</span></span> <span class="qt-pl-k" style="box-sizing:border-box;color:var(--color-prettylights-syntax-keyword);"><$</span> logger2,<br></td></tr><tr style="box-sizing:border-box;"><td id="qt-L1588" class="qt-blob-num qt-js-line-number qt-js-code-nav-line-number qt-js-blob-rnum" style="box-sizing:border-box;padding-top:0px;padding-right:10px;padding-bottom:0px;padding-left: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;"><br></td><td id="qt-LC1588" class="qt-blob-code qt-blob-code-inner qt-js-file-line" style="box-sizing:border-box;padding-top:0px;padding-right:10px;padding-bottom:0px;padding-left:10px;position:relative;line-height:20px;vertical-align:top;overflow-x:visible;overflow-y:visible;font-family:ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;font-size:12px;color:var(--color-fg-default);overflow-wrap:normal;white-space:pre;"><span class="qt-pl-s" style="box-sizing:border-box;color:var(--color-prettylights-syntax-string);"><span class="qt-pl-pds" style="box-sizing:border-box;color:var(--color-prettylights-syntax-string);">"</span>reporter died<span class="qt-pl-pds" style="box-sizing:border-box;color:var(--color-prettylights-syntax-string);">"</span></span> <span class="qt-pl-k" style="box-sizing:border-box;color:var(--color-prettylights-syntax-keyword);"><$</span> reporter,<br></td></tr><tr style="box-sizing:border-box;"><td id="qt-L1589" class="qt-blob-num qt-js-line-number qt-js-code-nav-line-number qt-js-blob-rnum" style="box-sizing:border-box;padding-top:0px;padding-right:10px;padding-bottom:0px;padding-left: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;"><br></td><td id="qt-LC1589" class="qt-blob-code qt-blob-code-inner qt-js-file-line" style="box-sizing:border-box;padding-top:0px;padding-right:10px;padding-bottom:0px;padding-left:10px;position:relative;line-height:20px;vertical-align:top;overflow-x:visible;overflow-y:visible;font-family:ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;font-size:12px;color:var(--color-fg-default);overflow-wrap:normal;white-space:pre;"><span class="qt-pl-s" style="box-sizing:border-box;color:var(--color-prettylights-syntax-string);"><span class="qt-pl-pds" style="box-sizing:border-box;color:var(--color-prettylights-syntax-string);">"</span>client died<span class="qt-pl-pds" style="box-sizing:border-box;color:var(--color-prettylights-syntax-string);">"</span></span> <span class="qt-pl-k" style="box-sizing:border-box;color:var(--color-prettylights-syntax-keyword);"><$</span> automation,<br></td></tr><tr style="box-sizing:border-box;"><td id="qt-L1590" class="qt-blob-num qt-js-line-number qt-js-code-nav-line-number qt-js-blob-rnum" style="box-sizing:border-box;padding-top:0px;padding-right:10px;padding-bottom:0px;padding-left: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;"><br></td><td id="qt-LC1590" class="qt-blob-code qt-blob-code-inner qt-js-file-line" style="box-sizing:border-box;padding-top:0px;padding-right:10px;padding-bottom:0px;padding-left:10px;position:relative;line-height:20px;vertical-align:top;overflow-x:visible;overflow-y:visible;font-family:ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;font-size:12px;color:var(--color-fg-default);overflow-wrap:normal;white-space:pre;"><span class="qt-pl-s" style="box-sizing:border-box;color:var(--color-prettylights-syntax-string);"><span class="qt-pl-pds" style="box-sizing:border-box;color:var(--color-prettylights-syntax-string);">"</span>outputter died<span class="qt-pl-pds" style="box-sizing:border-box;color:var(--color-prettylights-syntax-string);">"</span></span> <span class="qt-pl-k" style="box-sizing:border-box;color:var(--color-prettylights-syntax-keyword);"><$</span> outputter,<br></td></tr><tr style="box-sizing:border-box;"><td id="qt-L1591" class="qt-blob-num qt-js-line-number qt-js-code-nav-line-number qt-js-blob-rnum" style="box-sizing:border-box;padding-top:0px;padding-right:10px;padding-bottom:0px;padding-left: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;"><br></td><td id="qt-LC1591" class="qt-blob-code qt-blob-code-inner qt-js-file-line" style="box-sizing:border-box;padding-top:0px;padding-right:10px;padding-bottom:0px;padding-left:10px;position:relative;line-height:20px;vertical-align:top;overflow-x:visible;overflow-y:visible;font-family:ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;font-size:12px;color:var(--color-fg-default);overflow-wrap:normal;white-space:pre;"><span class="qt-pl-s" style="box-sizing:border-box;color:var(--color-prettylights-syntax-string);"><span class="qt-pl-pds" style="box-sizing:border-box;color:var(--color-prettylights-syntax-string);">"</span>calendar died<span class="qt-pl-pds" style="box-sizing:border-box;color:var(--color-prettylights-syntax-string);">"</span></span> <span class="qt-pl-k" style="box-sizing:border-box;color:var(--color-prettylights-syntax-keyword);"><$</span> calendarChecker<br></td></tr><tr style="box-sizing:border-box;"><td id="qt-L1592" class="qt-blob-num qt-js-line-number qt-js-code-nav-line-number qt-js-blob-rnum" style="box-sizing:border-box;padding-top:0px;padding-right:10px;padding-bottom:0px;padding-left: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;"><br></td><td id="qt-LC1592" class="qt-blob-code qt-blob-code-inner qt-js-file-line" style="box-sizing:border-box;padding-top:0px;padding-right:10px;padding-bottom:0px;padding-left:10px;position:relative;line-height:20px;vertical-align:top;overflow-x:visible;overflow-y:visible;font-family:ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;font-size:12px;color:var(--color-fg-default);overflow-wrap:normal;white-space:pre;">]<br></td></tr><tr style="box-sizing:border-box;"><td id="qt-L1593" class="qt-blob-num qt-js-line-number qt-js-code-nav-line-number qt-js-blob-rnum" style="box-sizing:border-box;padding-top:0px;padding-right:10px;padding-bottom:0px;padding-left: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;"><br></td><td id="qt-LC1593" class="qt-blob-code qt-blob-code-inner qt-js-file-line" style="box-sizing:border-box;padding-top:0px;padding-right:10px;padding-bottom:0px;padding-left:10px;position:relative;line-height:20px;vertical-align:top;overflow-x:visible;overflow-y:visible;font-family:ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;font-size:12px;color:var(--color-fg-default);overflow-wrap:normal;white-space:pre;"><span class="qt-pl-c1" style="box-sizing:border-box;color:var(--color-prettylights-syntax-constant);">print</span> err<br></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. <br></div><div dir="ltr"><br></div><div dir="ltr"><br></div><div dir="ltr">    <br></div><div dir="ltr"><div><br></div><blockquote type="cite"><div>On Mar 9, 2022, at 08:10, Olaf Klinke <olf@aatal-apotheke.de> wrote:<br></div></blockquote></div><blockquote type="cite"><div dir="ltr"><div><span>On Mon, 2022-03-07 at 19:59 +0000, coot@coot.me wrote:</span><br></div><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><div><span></span><br></div><div><span>Thanks for pointing this out, Marcin. </span><br></div><div><span>Async seems to offer much better abstractions than what GHC.Conc</span><br></div><div><span>provides for  ThreadId. </span><br></div><div><span>I have the impression, though, that Async was written for threads that</span><br></div><div><span>are supposed to do their work and eventually terminate. </span><br></div><div><span>In my application, a webserver forks several perpetually running</span><br></div><div><span>threads and offers supervision to the user. Therefore withAsync is not</span><br></div><div><span>perfectly suited, as we do not know upfront when and what we're going</span><br></div><div><span>to do with the Async handle. I resorted to the following pattern. </span><br></div><div><span></span><br></div><div><span>import Control.Concurrent.Async</span><br></div><div><span>import Control.Concurrent</span><br></div><div><span>import Control.Exception (SomeException)</span><br></div><div><span></span><br></div><div><span>type MyThread = (IO (),MVar (Async ()))</span><br></div><div><span></span><br></div><div><span>startThread :: MyThread -> IO ()</span><br></div><div><span>startThread (action,var) = withAsync action (putMVar var)</span><br></div><div><span></span><br></div><div><span>pauseThread :: MyThread -> IO ()</span><br></div><div><span>pauseThread (_,var) = do</span><br></div><div><span>    a <- takeMVar var</span><br></div><div><span>    cancel a</span><br></div><div><span></span><br></div><div><span>data MyThreadStatus = Paused | Running | Died SomeException</span><br></div><div><span>threadStatus :: MyThread -> IO MyThreadStatus</span><br></div><div><span>threadStatus (_,var) = do</span><br></div><div><span>    running <- tryReadMVar var</span><br></div><div><span>    case running of</span><br></div><div><span>        Nothing -> return Paused</span><br></div><div><span>        Just a -> do</span><br></div><div><span>            finished <- poll a</span><br></div><div><span>            case finished of</span><br></div><div><span>                Nothing         -> return Running</span><br></div><div><span>                Just (Right _)  -> return Paused</span><br></div><div><span>                Just (Left why) -> return (Died why)</span><br></div><div><span></span><br></div><div><span>-- Olaf</span><br></div><div><span></span><br></div><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><div><span></span><br></div><div><span></span><br></div><div><span>_______________________________________________</span><br></div><div><span>Haskell-Cafe mailing list</span><br></div><div><span>To (un)subscribe, modify options or view archives go to:</span><br></div><div><span>http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe</span><br></div><div><span>Only members subscribed via the mailman list are allowed to post.</span><br></div></div></blockquote><div>_______________________________________________<br></div><div>Haskell-Cafe mailing list<br></div><div>To (un)subscribe, modify options or view archives go to:<br></div><div><a href="http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe">http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe</a><br></div><div>Only members subscribed via the mailman list are allowed to post.<br></div></blockquote><div><br></div></body></html>