1<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" 2 xmlns:content="http://purl.org/rss/1.0/modules/content/" 3 xmlns:wfw="http://wellformedweb.org/CommentAPI/" 4 xmlns:dc="http://purl.org/dc/elements/1.1/" 5 xmlns:atom="http://www.w3.org/2005/Atom" 6 xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" 7 xmlns:slash="http://purl.org/rss/1.0/modules/slash/" 8 > 9 10<channel> 11 <title>Singpolyma</title> 12 <atom:link href="https://singpolyma.net/feed/" rel="self" type="application/rss+xml" /> 13 <link>https://singpolyma.net</link> 14 <description></description> 15 <lastBuildDate>Thu, 17 Sep 2026 01:19:31 +0000</lastBuildDate> 16 <language>en-US</language> 17 <sy:updatePeriod>hourly</sy:updatePeriod> 18 <sy:updateFrequency>1</sy:updateFrequency> 19 <generator>https://wordpress.org/?v=5.0.15</generator> 20<atom:link rel="hub" href="http://pubsubhubbub.appspot.com"/><atom:link rel="hub" href="https://singpolyma.superfeedr.com"/><image><title>Singpolyma</title><url>http://www.gravatar.com/avatar/3ab4d3a66e470ce10eb7ec812fab3c46</url><link>https://singpolyma.net</link><width>80</width><height>80</height><description>Singpolyma - https://singpolyma.net</description></image><atom:link rel='salmon' href='https://singpolyma.net/wordpress/?salmon=endpoint'/><atom:link rel='http://salmon-protocol.org/ns/salmon-replies' href='https://singpolyma.net/wordpress/?salmon=endpoint'/><atom:link rel='http://salmon-protocol.org/ns/salmon-mention' href='https://singpolyma.net/wordpress/?salmon=endpoint'/> <item> 21 <title>Scarcity</title> 22 <link>https://singpolyma.net/2026/09/scarcity/</link> 23 <comments>https://singpolyma.net/2026/09/scarcity/#comments</comments> 24 <pubDate>Wed, 16 Sep 2026 21:04:00 +0000</pubDate> 25 <dc:creator><![CDATA[Stephen Paul Weber]]></dc:creator> 26 <category><![CDATA[Abundance]]></category> 27 <category><![CDATA[Politics]]></category> 28 29 <guid isPermaLink="false">https://singpolyma.net/?p=567659</guid> 30 <description><![CDATA[I’ve been thinking a lot this year about inequality and technology and how we got here and where we might be going. Why is school free but not food? Why do some forms of automation help people while others hurt them? Is it even possible for us to reach a post-scarcity world? “Good” Automation I […]]]></description> 31 <content:encoded><![CDATA[<p>I’ve been thinking a lot this year about inequality and technology and how we got here and where we might be going. Why is school free but not food? Why do some forms of automation help people while others hurt them? Is it even possible for us to reach a <a href="https://en.wikipedia.org/wiki/Post-scarcity">post-scarcity</a> world?</p> 32<h2>“Good” Automation</h2> 33<p>I don’t hear a lot of people complain about the existence of their dishwasher, washing machine, barbecue, or vacuum cleaner. They may complain when they don’t work well, but when they do work it seems like these automations are well-liked. Not many yearn to spend more hours in their day doing laundry or starting fires.</p> 34<p>These automations mostly have something in common: they are used to automate a task the person believes would otherwise be done by themselves, and which they’d rather not do. They are often also owned by the person, but laundromats and other forms of rented automation still improve the person’s life.</p> 35<p>Yet these automations all exist at the expense of jobs which used to exist. These tasks were not always done by a person for themselves, and many fewer people are paid to do these tasks now that machines can do them. I do think, however, that most people imagine that they are not in the class that would ever have employed someone to do this work (even if they are) and so it is viewed almost entirely as personal labour-saving and not labour-destroying.</p> 36<h2>Jobs</h2> 37<p>“At the expense of jobs” is quite an idea. Implicit in this is the idea that “having a job” is good or even necessary. When someone who up until this point has been employed scrubbing clothes is displaced by a washer, is it the hours spent scrubbing that they miss? I rather expect that what they miss is income. Fundamentally, perhaps not even income but the things that an income can provide: food, shelter, etc.</p> 38<p>People often talk about automations which are viewed as labour-destroying rather than labour-saving in terms like “more jobs will be created” etc. I think this whole discussion misses the point. Jobs are not what we want. Money is barely what we want. What we want is food.</p> 39<h2>“Bad” Automation</h2> 40<p>In fact, this hyper-focus on employment is, I believe, a key factor in what makes many automations ultimately “bad” for us. The cycle is something like this: a new automation comes out which allows a person to accomplish the same work in less t
40ime. Or, viewed another way, to accomplish more in the same time. Or, viewed another way, for a smaller group to accomplish what a larger group used to accomplish.</p> 41<p>If our driving force is that everyone must be fully-employed, then accomplishing the same amount in less time is no good, since that leaves unemployed time. Accomplishing more in the same time is fine, but who gets the benefit of this “more” that is accomplished? Rarely in our existing system is the answer the person themselves. And what if their job has nothing “more” for them to do? Well then we shrink the group, which shrinks expenses for the employer. Who benefits from that? Neither the one who left nor those who remain.</p> 42<h2>The Replicator Problem</h2> 43<p>Imagine that tomorrow is announced the first viable <a href="https://en.wikipedia.org/wiki/Replicator_(Star_Trek)">replicator</a>. Post-scarcity is here! All else being equal, what will happen? Every production job is gone, most transport jobs, and many others besides. Some corporation buys the patent to the replicator, and they are the only ones operating them. Maybe they sell them, and you have to buy the “design files” for items to be produced from them.</p> 44<p>I do not believe that post-scarcity is being prevented by a lack of technological advancement. Not when every advancement is owned and the bulk of humanity is driven to continue seeking jobs forever. Even with limitless energy and replicator technology, people would now be looking for what job they can do to afford to operate the replicator.</p> 45<p>Indeed we are already closer to post-scarcity than we often like to admit. Energy is plentiful and cheap (and, increasingly, clean), automation is widespread, a handful of people compared to the historical norm now produce all of the food we all eat (and throw out) every day.</p> 46<h2>Post-Jobs</h2> 47<p>Therefore, I believe in order to reach post-scarcity we must first target post-jobs. This may seem backwards: isn’t post-scarcity supposed to be what makes it possible for us to go post-jobs? By post-jobs I don’t mean that no one ever works again doing anything productive (indeed, even post-scarcity would never mean that; humans love doing things) but rather decoupling that labour from the things people want and need: especially fundamentals like food, housing, etc.</p> 48<p>Of course this is nothing new. Many different ideas have been proposed for this decoupling. Under capitalism, the idea of “basic income” has become very popular as a way of keeping the system intact while starting this decoupling.</p> 49<p>So what can I do? What can you do? How do we make real progress and not just technological progress? I don’t know. If history has taught me anything it may be that we cannot. Certainly nothing any one of us does will accomplish much. Like every important problem stemming from this, it is ultimately a collective action problem, and we are if anything getting <strong>worse</strong> at acting collectively, not better. So do we give up? Of course not. But we need to be thinking about the whole picture if we want to get to a future where people work less and have more, rather than the opposite.</p> 50 <p class="ccline"><a href="http://creativecommons.org/licenses/by/4.0/" rel="license"><img alt="Creative Commons Attribution 4.0 International License" src="http://i.creativecommons.org/l/by/4.0/88x31.png" /></a> © 2006-2026 Stephen Paul Weber. Some Rights Reserved.</p>]]></content:encoded> 51 <wfw:commentRss>https://singpolyma.net/2026/09/scarcity/feed/</wfw:commentRss> 52 <slash:comments>1</slash:comments> 53 </item> 54 <item> 55 <title>Priority Continuum Onyx Unboxing and Assembly (Photos)</title> 56 <link>https://singpolyma.net/2019/01/priority-continuum-onyx-unboxing-and-assembly-photos/</link> 57 <comments>https://singpolyma.net/2019/01/priority-continuum-onyx-unboxing-and-assembly-photos/#comments</comments> 58 <pubDate>Tue, 08 Jan 2019 17:05:23 +0000</pubDate> 59 <dc:creator><![CDATA[Stephen Paul Weber]]></dc:creator> 60 <category><![CDATA[Bicycles]]></category> 61 62 <guid isPermaLink="false">https://singpolyma.net/?p=566913</guid>
63 <description><![CDATA[© 2006-2026 Stephen Paul Weber. Some Rights Reserved.]]></description> 64 <content:encoded><![CDATA[ 65<a href='https://singpolyma.net/actionstream/new-bike/img_20190107_182040/'><img width="300" height="300" src="https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_182040-300x300.jpg" class="attachment-medium size-medium" alt="" srcset="https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_182040-300x300.jpg 300w, https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_182040-150x150.jpg 150w, https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_182040-768x768.jpg 768w, https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_182040-1024x1024.jpg 1024w" sizes="(max-width: 300px) 100vw, 300px" /></a> 66<a href='https://singpolyma.net/2019/01/priority-continuum-onyx-unboxing-and-assembly-photos/img_20190107_185516/'><img width="300" height="300" src="https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_185516-300x300.jpg" class="attachment-medium size-medium" alt="" srcset="https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_185516-300x300.jpg 300w, https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_185516-150x150.jpg 150w, https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_185516-768x768.jpg 768w, https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_185516-1024x1024.jpg 1024w" sizes="(max-width: 300px) 100vw, 300px" /></a> 67<a href='https://singpolyma.net/2019/01/priority-continuum-onyx-unboxing-and-assembly-photos/img_20190107_190311/'><img width="300" height="300" src="https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_190311-300x300.jpg" class="attachment-medium size-medium" alt="" srcset="https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_190311-300x300.jpg 300w, https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_190311-150x150.jpg 150w, https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_190311-768x768.jpg 768w, https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_190311-1024x1024.jpg 1024w" sizes="(max-width: 300px) 100vw, 300px" /></a> 68<a href='https://singpolyma.net/2019/01/priority-continuum-onyx-unboxing-and-assembly-photos/img_20190107_191438/'><img width="300" height="300" src="https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_191438-300x300.jpg" class="attachment-medium size-medium" alt="" srcset="https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_191438-300x300.jpg 300w, https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_191438-150x150.jpg 150w, https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_191438-768x768.jpg 768w, https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_191438-1024x1024.jpg 1024w" sizes="(max-width: 300px) 100vw, 300px" /></a> 69<a href='https://singpolyma.net/2019/01/priority-continuum-onyx-unboxing-and-assembly-photos/img_20190107_193310/'><img width="300" height="300" src="https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_193310-300x300.jpg" class="attachment-medium size-medium" alt="" srcset="https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_193310-300x300.jpg 300w, https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_193310-150x150.jpg 150w, https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_193310-768x768.jpg 768w, https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_193310-1024x1024.jpg 1024w" sizes="(max-width: 300px) 100vw, 300px" /></a> 70<a href='https://singpolyma.net/2019/01/priority-continuum-onyx-unboxing-and-assembly-photos/img_20190107_193914/'><img width="300" height="300" src="https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_193914-300x300.jpg" class="attachment-medium size-medium" alt="" srcset="https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_193914-300x300.jpg 300w, https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_193914-150x150.jpg 150w, https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_193914-768x768.jpg 768w, https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_193914-1024x1024.jpg 1024w" sizes="(max-width: 300px) 100v
70w, 300px" /></a> 71<a href='https://singpolyma.net/2019/01/priority-continuum-onyx-unboxing-and-assembly-photos/img_20190107_194454/'><img width="300" height="300" src="https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_194454-300x300.jpg" class="attachment-medium size-medium" alt="" srcset="https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_194454-300x300.jpg 300w, https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_194454-150x150.jpg 150w, https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_194454-768x768.jpg 768w, https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_194454-1024x1024.jpg 1024w" sizes="(max-width: 300px) 100vw, 300px" /></a> 72<a href='https://singpolyma.net/2019/01/priority-continuum-onyx-unboxing-and-assembly-photos/img_20190107_195948/'><img width="300" height="300" src="https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_195948-300x300.jpg" class="attachment-medium size-medium" alt="" srcset="https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_195948-300x300.jpg 300w, https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_195948-150x150.jpg 150w, https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_195948-768x768.jpg 768w, https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_195948-1024x1024.jpg 1024w" sizes="(max-width: 300px) 100vw, 300px" /></a> 73<a href='https://singpolyma.net/2019/01/priority-continuum-onyx-unboxing-and-assembly-photos/img_20190107_203816/'><img width="300" height="300" src="https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_203816-300x300.jpg" class="attachment-medium size-medium" alt="" srcset="https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_203816-300x300.jpg 300w, https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_203816-150x150.jpg 150w, https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_203816-768x768.jpg 768w, https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190107_203816-1024x1024.jpg 1024w" sizes="(max-width: 300px) 100vw, 300px" /></a> 74<a href='https://singpolyma.net/2019/01/priority-continuum-onyx-unboxing-and-assembly-photos/img_20190108_113218/'><img width="300" height="300" src="https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190108_113218-300x300.jpg" class="attachment-medium size-medium" alt="" srcset="https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190108_113218-300x300.jpg 300w, https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190108_113218-150x150.jpg 150w, https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190108_113218-768x768.jpg 768w, https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190108_113218-1024x1024.jpg 1024w" sizes="(max-width: 300px) 100vw, 300px" /></a> 75<a href='https://singpolyma.net/2019/01/priority-continuum-onyx-unboxing-and-assembly-photos/img_20190108_113303/'><img width="300" height="300" src="https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190108_113303-300x300.jpg" class="attachment-medium size-medium" alt="" srcset="https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190108_113303-300x300.jpg 300w, https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190108_113303-150x150.jpg 150w, https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190108_113303-768x768.jpg 768w, https://singpolyma.net/wordpress/wp-content/uploads/2019/01/IMG_20190108_113303-1024x1024.jpg 1024w" sizes="(max-width: 300px) 100vw, 300px" /></a> 76 77 <p class="ccline"><a href="http://creativecommons.org/licenses/by/4.0/" rel="license"><img alt="Creative Commons Attribution 4.0 International License" src="http://i.creativecommons.org/l/by/4.0/88x31.png" /></a> © 2006-2026 Stephen Paul Weber. Some Rights Reserved.</p>]]></content:encoded> 78 <wfw:commentRss>https://singpolyma.net/2019/01/priority-continuum-onyx-unboxing-and-assembly-photos/feed/</wfw:commentRss> 79 <slash:comments>5</slash:comments> 80 </item> 81 <item> 82 <title>Rust Factory Without Box (Trait Object)</title> 83 <link>https://singpolyma.net/2018/09/rust-factory-without-box-trait-object/</link> 84 <comments>https://singpolyma.net/2018/09/rust-factory-without-box-trait-object/#comments</comments> 85 <pubDate>Sat, 01 Sep 2018 17:52:00 +0000</pubDate> 86 <dc:creator><![CDATA[Stephen Paul Weber]]></dc:creator> 87 <category><![CDATA[Rust]]></category> 88 <category><![CDATA[Tech]]></category> 89 90 <guid isPermaLink="false">https://singpolyma.net/?p=566889</guid>
91 <description><![CDATA[I’ve been playing around a lot with Rust recently and it’s quickly becoming my second-favourite programming language. One of the things I’ve been playing with is some Object Oriented design concepts as they might apply. For example, consider this code: fn format_year(n: i32) -> String { if n == 0 { "0 is not a […]]]></description> 92 <content:encoded><![CDATA[<p>I’ve been playing around a lot with <a href="https://www.rust-lang.org/">Rust</a> recently and it’s quickly becoming my second-favourite programming language. One of the things I’ve been playing with is some Object Oriented design concepts as they might apply. For example, consider this code:</p> 93<pre><code>fn format_year(n: i32) -> String { 94 if n == 0 { 95 "0 is not a year".to_string() 96 } else if n < 0 { 97 format!("{} BC", -n) 98 } else { 99 format!("{} AD", n) 100 } 101}</code></pre> 102<p>While maybe overkill for this small example, let’s go ahead and <a href="https://refactoring.guru/replace-conditional-with-polymorphism">replace conditional with polymorphism</a>:</p> 103<pre><code>fn format_year(n: Box<Year>) -> String { 104 format!("{} {}", n.year(), n.era()) 105} 106 107trait Year { 108 fn year(&self) -> u32; 109 fn era(&self) -> String; 110} 111 112impl Year { 113 fn new(n: i32) -> Box<Year> { 114 if n == 0 { 115 Box::new(YearZero()) 116 } else if n < 0 { 117 Box::new(YearBC(-n as u32)) 118 } else { 119 Box::new(YearAD(n as u32)) 120 } 121 } 122} 123 124struct YearZero(); 125 126impl Year for YearZero { 127 fn year(&self) -> u32 { 0 } 128 fn era(&self) -> String { "is not a year".to_string() } 129} 130 131struct YearBC(u32); 132 133impl Year for YearBC { 134 fn year(&self) -> u32 { self.0 } 135 fn era(&self) -> String { "BC".to_string() } 136} 137 138struct YearAD(u32); 139 140impl Year for YearAD { 141 fn year(&self) -> u32 { self.0 } 142 fn era(&self) -> String { "AD".to_string() } 143}</code></pre> 144<p>This works, and really does seem to mimic the way this kind of design looks in a class-based Object Oriented language. It has a major disadvantage, however: all our objects are on the heap now (which is likely to cause performance issues). In some cases, this can be fixed by using <a href="https://en.wikipedia.org/wiki/Continuation-passing_style">CPS</a> so that the trait objects could be borrowed references instead of boxed, but that’s both ugly and not always an option. One other design might be to use an enum:</p> 145<pre><code>fn format_year(n: Year) -> String { 146 format!("{} {}", n.year(), n.era()) 147} 148 149enum Year { 150 YearZero, 151 YearBC(u32), 152 YearAD(u32) 153} 154 155impl Year { 156 fn new(n: i32) -> Year { 157 if n == 0 { 158 Year::YearZero 159 } else if n < 0 { 160 Year::YearBC(-n as u32) 161 } else { 162 Year::YearAD(n as u32) 163 } 164 } 165 166 fn year(&self) -> u32 { 167 match self { 168 YearZero => 0, 169 YearBC(y) => y, 170 YearAD(y) => y 171 } 172 } 173 174 fn era(&self) -> u32 { 175 match self { 176 YearZero => "is not a year".to_string(), 177 YearBC(y) => "BC".to_string(), 178 YearAD(y) => "AD".to_string() 179 } 180 } 181}</code></pre> 182<p>No more heap allocations! While this is obviously analogous, some might claim we haven’t actually “replaced conditional” at all, though we have at least contained the conditionals in a place where a type only knows about itself, and not about other things that might get passed in. Even if you accept adding match arms on <code>self</code> as “extension”, in terms of <a href="https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle">open/closed</a> this requires a modification to at <em>least</em> the enum and the factory to add a new case, instead of just the factory as with the trait version.</p> 183<p>What is it about the enum version that allows us to avoid the boxing? Well, an enum knows what all the possibilities are, and so the compiler can know the size that needs to be reserved to store any one of those. With the trait case, the compiler can’t know how big the infinite world of possibilities that might implement that trait could be, and so cannot know the size to be reserved: we have to defer that to runtime and use a box. <em>However</em>, the factory will always actually return only a known list of trait implementations… can we exploit that to know the size somehow? What if we create an enum of the structs from the trait version and have the factory return <em>that</em>?</p> 184<pre><code>enum YearEnum { 185 YearZero(YearZero), 186 YearBC(YearBC), 187 YearAD(YearAD) 188} 189 190impl Year { 191 fn new(n: i32) -> YearEnum { 192 if n == 0 { 193 YearEnum::YearZero(YearZero()) 194 } else if n < 0 { 195 YearEnum::YearBC(YearBC(-n as u32)) 196 } else { 197 YearEnum::YearAD(YearAD(n as u32)) 198 } 199 } 200} 201 202impl std::ops::Deref for YearEnum { 203 type Target = Year; 204 205 fn deref(&self) -> &Self::Target { 206 match self { 207 YearEnum::YearZero(x) => x, 208 YearEnum::YearBC(x) => x, 209 YearEnum::YearAD(x) => x 210 } 211 } 212}</code></pre> 213<p>The <code>impl std::ops::Deref</code> will allow us to call any method in the <code>Year</code> trait on the enum as returned from the factory, allowing this to effectively act as a trait object, but with no heap allocations! This seems like exactly what we want, but it’s a lot of boilerplate. Luckily, it’s very mechanical so creating a macro to do this for us is fairly easy (and I’ll throw in a bunch of other obvious trait implementations while we’re at it):</p> 214<pre><code>macro_rules! trait_enum { 215 ($trait:ident, $enum:ident, $( $item:ident ) , *) => { 216 enum $enum { 217 $( 218 $item($item), 219 )* 220 } 221 222 impl std::ops::Deref for $enum { 223 type Target = $trait; 224 225 fn deref(&self) -> &Self::Target { 226 match self { 227 $( 228 $enum::$item(x) => x, 229 )* 230 } 231 } 232 } 233 234 impl From<$enum> for Box<$trait> { 235 fn from(input: $enum) -> Self { 236 match input { 237 $( 238 $enum::$item(x) => Box::new(x), 239 )* 240 } 241 } 242 } 243 244 impl<'a> From<&'a $enum> for &'a $trait { 245 fn from(input: &'a $enum) -> Self { 246 &**input 247 } 248 } 249 250 impl<'a> AsRef<$trait + 'a> for $enum { 251 fn as_ref(&self) -> &($trait + 'a) { 252 &**self 253 } 254 } 255 256 impl<'a> std::borrow::Borrow<$trait + 'a> for $enum { 257 fn borrow(&self) -> &($trait + 'a) { 258 &**self 259 } 260 } 261 262 $( 263 impl From<$item> for $enum { 264 fn from(input: $item) -> Self { 265 $enum::$item(input) 266 } 267 } 268 )* 269 } 270}</code></pre> 271<p>And now to repeat the first refactoring, but with the help of this new macro:</p> 272<pre><code>fn format_year<Y: Year + ?Sized>(n: &Y) -> String { 273 format!("{} {}", n.year(), n.era()) 274} 275 276trait Year { 277 fn year(&self) -> u32; 278 fn era(&self) -> String; 279} 280 281trait_enum!(Year, YearEnum, YearZero, YearBC, YearAD); 282 283impl Year { 284 fn new(n: i32) -> YearEnum { 285 if n == 0 { 286 YearZero().into() 287 } else if n < 0 { 288 YearBC(-n as u32).into() 289 } else { 290 YearAD(n as u32).into() 291 } 292 } 293} 294 295struct YearZero(); 296 297impl Year for YearZero { 298 fn year(&self) -> u32 { 0 } 299 fn era(&self) -> String { "is not a year".to_string() } 300} 301 302struct YearBC(u32); 303 304impl Year for YearBC { 305 fn year(&self) -> u32 { self.0 } 306 fn era(&self) -> String { "BC".to_string() } 307} 308 309struct YearAD(u32); 310 311impl Year for YearAD { 312 fn year(&self) -> u32 { self.0 } 313 fn era(&self) -> String { "AD".to_string() } 314}</code></pre> 315<p>We do still have two places with must be modified rather than extended (the macro invocation and the factory), but all other code
315can be written ignorant of those and in the same style as using a normal trait object. The normal trait objects can even be recovered using various implementations the macro creates, or even just by doing <code>&*</code> on the enum. Benchmarking these three styles on a somewhat more complex example actually found this last one to also be the most performant (though only marginally faster than the pure-enum approach), and the boxed-trait-object style to be more than three times slower.</p> 316<p>So there you go, next time you ask yourself if you want the flexibility of a trait or the size guarantees and performance of an enum, maybe grab a macro and say: why not both!</p> 317 <p class="ccline"><a href="http://creativecommons.org/licenses/by/4.0/" rel="license"><img alt="Creative Commons Attribution 4.0 International License" src="http://i.creativecommons.org/l/by/4.0/88x31.png" /></a> © 2006-2026 Stephen Paul Weber. Some Rights Reserved.</p>]]></content:encoded> 318 <wfw:commentRss>https://singpolyma.net/2018/09/rust-factory-without-box-trait-object/feed/</wfw:commentRss> 319 <slash:comments>5</slash:comments> 320 </item> 321 <item> 322 <title>Error Handling in Haskell</title> 323 <link>https://singpolyma.net/2018/05/error-handling-in-haskell/</link> 324 <comments>https://singpolyma.net/2018/05/error-handling-in-haskell/#comments</comments> 325 <pubDate>Wed, 16 May 2018 01:48:27 +0000</pubDate> 326 <dc:creator><![CDATA[Stephen Paul Weber]]></dc:creator> 327 <category><![CDATA[Exceptions]]></category> 328 <category><![CDATA[Haskell]]></category> 329 <category><![CDATA[Tech]]></category> 330 331 <guid isPermaLink="false">https://singpolyma.net/?p=566861</guid> 332 <description><![CDATA[When I first started learning Haskell, I learned about the Monad instance for Either and immediately got excited. Here, at long last, was a good solution to the error handling problem. When you want exception-like semantics, you can have them, and the rest of the time it’s just a normal value. Later, I learned that […]]]></description> 333 <content:encoded><![CDATA[<p>When I first started learning Haskell, I learned about the <code>Monad</code> instance for <code>Either</code> and immediately got excited. Here, at long last, was a <em>good</em> solution to the error handling problem. When you want exception-like semantics, you can have them, and the rest of the time it’s just a normal value. Later, I learned that the Haskell standard <em>also</em> includes an <a href="https://www.haskell.org/onlinereport/haskell2010/haskellch42.html#x50-34100042">exception mechanism</a> for the <code>IO</code> type. I was horrified, and confused, but nothing could have prepared me for what I discovered next.</p> 334<p>While the standard Haskell exception mechanism infects all of <code>IO</code> it at least has a single, well-defined type for possible errors, with a small number of known cases to handle. <a href="https://hackage.haskell.org/package/base/docs/Control-Exception.html">GHC extends this</a> with a <em>dynamically typed</em> exception system where any <code>IO</code> value may be hiding any number of unknown and unknowable exception types! Additionally, all manner of programmer errors in pure code (such as pattern match failures and integer division by zero) are thrown into <code>IO</code> when they get used in that context. On top of everything, so-called exceptions can appear that <strong>were not thrown by any code you can see</strong> but are external to you and your dependencies entirely. There are two classes of these: asynchronous exceptions thrown by the runtime due to a failure in the runtime itself (such as a <code>HeapOverflow</code>) and exceptions thrown due to some impossible-to-meet condition the runtime detects (such as detectable non-termination). Oh, I almost forgot, manually <a href="https://hackage.haskell.org/package/base/docs/Control-Concurrent.html#v:killThread">killing a thread</a> or <a href="https://hackage.haskell.org/package/base/docs/System-Exit.html">telling the process to exit</a> are also modeled as “exceptions” by GHC.</p> 335<p>Once the initial decision to have a dynamically typed exception system was made, everything that could make use of an exception-like semantic in any case was bolted on. What am I going to do, though? Write my own ecosystem and runtime that works how I would prefer? No, I’m going to find a way to make the best of the world I’m in.</p> 336<p>When dealing with this situation, there are two separate and equally important things to consider: exception safety, and error handling. Exception safety describes the situation when you are, for example, acquiring and releasing resources (such as file handles). You want to be sure you release the resource, even if the exception system is going to abort your computation unceremoniously. You can <strong>never</strong> know if this will happen or not, since the runtime can just throw things at you, you <strong>always</strong> need to wrap resource acquisition/release paths in some exception safety. There are a lot of complex issues here, but it’s not the subject of this post so suffice to say the main pattern of interest for dealing with this is <a href="https://hackage.haskell.org/package/base/docs/Control-Exception.html#v:bracket">bracket</a>.</p> 337<p>Error handling is totally different. This is where you want to be able to re
337cover from possible recoverable errors and do something sensible. Retry, save the task for later, alert the user that their request failed, read from cache when the network is down, whatever.</p> 338<p>The first move in this area that I saw that I liked, was the <a href="https://hackage.haskell.org/package/errors">errors</a> package. Many helpers for dealing with error values, and in earlier versions a <a href="https://hackage.haskell.org/package/errors-2.0.0/docs/src/Control-Error-Util.html#syncIO">helper that would exclude unrecoverable errors</a> and convert the rest to error values. I liked this pattern, but wanted more. This is Haskell! I wanted to <em>know</em>, at a type level, when recoverable errors had already been handled. Of course, programmer errors in pure code and unrecoverable errors from the runtime are always possible, so we can’t say anything about them at the type level, but recoverable errors we could know something about. So I wrote <a href="https://hackage.haskell.org/package/unexceptionalio">a package</a>, iterated a few times, and eventually became a <em>dependency</em> for the helper in the errors package that I had based my whole idea on. Until very recently, errors and unexceptionalio were the two ways I was aware of to handle recoverable errors (and <em>only</em> recoverable errors) reliably, and know at a type level that you had done so. Recently errors decided to change the semantic to fit the previously-misleading documentation of the helper and so unexceptionalio now stands alone (to my knowledge) in this area.</p> 339<p>In light of this new reality, I updated the package to make it much more clear (both in documentation and at a type level) what hole in the ecosystem this fills. I exposed the semantic in a few more ways so it can be useful even to people who don’t care about type-level error information. I also named the unrecoverable errors. Things you might want to be safe from, or maybe log and terminate a thread because of, but never recover from. For now, I call these <code>PseudoException</code>.</p> 340<p><a href="https://hackage.haskell.org/package/unexceptionalio/docs/UnexceptionalIO.html">UnexceptionalIO</a> (when used on GHC) now exposes four instances of <code>Exception</code> that you can use even if you have no use for the rest of the package: <code>ExternalError</code> (for things the runtime throws at you, asynchronously or not), <code>ProgrammerError</code> (for things raised from mistakes in pure code), <code>PseudoException</code> (includes the above and also requests for the process to exit), and <code>SomeNonPseudoException</code> (the negation of the above). All of these will work with the normal GHC catch mechanisms to allow you to easily separate these different uses for the exception system.</p> 341<p>From there, the package builds up a type and typeclass with entry and exit points that ensure that values in this type contain no <code>SomeNonPseudoException</code>. The type is only ever used in argument position, all return types are polymorphic in the typeclass (implemented for both <code>UIO</code> and <code>IO</code>, as well as for all monad transformers in the unexceptional-trans package) so that you can use them without having to commit to <code>UIO</code> for your code. If you use the helpers in a program that is all based on <code>IO</code> still, it will catch the exceptions and continue just fine in that context.</p> 342<p>Finally, the latest version of the package also exposes some helpers for those cases where you <em>do</em> want to do something with <code>PseudoException</code>. For exception safety, of course, there is <code>bracket</code>, specialized to <code>UIO</code>. For knowing when a thread has terminated (for any reason, success or failure) there is <code>forkFinally</code>, specialized to <code>UIO</code> and <code>PseudoException</code>. Finally, to make sure you don’t accidentally swallow any <code>PseudoException</code> when running a thread, there is <code>fork</code> which will ignore <code>ThreadKilled</code> (you assumedly did that on purpose) but otherwise rethrow <code>PseudoException</code> that terminate a thread to the parent thread.</p> 343<p>This is hardly the final word in error handling, but for me, this provides enough sanity that I can handle what I need to in different applications and express my errors at a type level when I want to.<
343/p> 344 <p class="ccline"><a href="http://creativecommons.org/licenses/by/4.0/" rel="license"><img alt="Creative Commons Attribution 4.0 International License" src="http://i.creativecommons.org/l/by/4.0/88x31.png" /></a> © 2006-2026 Stephen Paul Weber. Some Rights Reserved.</p>]]></content:encoded> 345 <wfw:commentRss>https://singpolyma.net/2018/05/error-handling-in-haskell/feed/</wfw:commentRss> 346 <slash:comments>5</slash:comments> 347 </item> 348 <item> 349 <title>Haskell2010 Dynamic Cast</title> 350 <link>https://singpolyma.net/2018/05/haskell2010-dynamic-cast/</link> 351 <comments>https://singpolyma.net/2018/05/haskell2010-dynamic-cast/#comments</comments> 352 <pubDate>Sat, 12 May 2018 16:47:58 +0000</pubDate> 353 <dc:creator><![CDATA[Stephen Paul Weber]]></dc:creator> 354 <category><![CDATA[Haskell]]></category> 355 <category><![CDATA[Tech]]></category> 356 357 <guid isPermaLink="false">https://singpolyma.net/?p=566853</guid> 358 <description><![CDATA[This post is not meant to be a suggestion that you should use this code for anything. I found the exploration educational and I’m sharing because I find the results interesting. This post is a Literate Haskell file. Programmers often mean different things when they say “cast”. One thing they sometimes mean is to be […]]]></description> 359 <content:encoded><![CDATA[<p>This post is not meant to be a suggestion that you should use this code for anything. I found the exploration educational and I’m sharing because I find the results interesting. This post is a Literate Haskell file.</p> 360<p>Programmers often mean different things when they say “cast”. One thing they sometimes mean is to be able to use a value of one type as another type, converting as possible.</p> 361<p>We’ll use dynamic typing to allow us to check the conversions at runtime.</p> 362<pre><code>> module DynamicCast (DynamicCastable(..), dynamicCast, Opaque) where 363> import Data.Dynamic 364> import Data.Void 365> import Control.Applicative 366> import Control.Monad 367> import Data.Typeable 368> import Text.Read (readMaybe) 369> import Data.Traversable (sequenceA)</code></pre> 370<p>But we don’t want to expose the dynamic typing outside of this module, in case people become confused and try to use a Dynamic they got from elsewhere. Really we’re just using the Dynamic as an opaque intermediate step.</p> 371<p><code>> newtype Opaque = Opaque Dynamic</code></p> 372<p>Types can define how they both enter and exit the intermediate representation. This both allows casting existing types to new types, but also can allow casting new types to existing types without changing the instances for those existing types.</p> 373<pre><code>> class (Typeable a) => DynamicCastable a where 374> toOpaque :: a -> Opaque 375> toOpaque = Opaque . toDyn 376> 377> fromOpaque :: Opaque -> Maybe a 378> fromOpaque (Opaque dyn) = fromDynamic dyn</code></pre> 379<p>And finally the cast itself.</p> 380<pre><code>> dynamicCast :: (DynamicCastable a, DynamicCastable b) => a -> Maybe b 381> dynamicCast = fromOpaque . toOpaque</code></pre> 382<p>Let’s see some examples.</p> 383<p>We’ll say that Integer and simple lists (including String) represent themselves and define no specific conversions.</p> 384<pre><code>> instance DynamicCastable Integer 385> instance (Typeable a) => DynamicCastable [a]</code></pre> 386<p>Int is represented however Integer represents itself.</p> 387<p>Anything that can convert to Integer can convert to Int.</p> 388<p>Any String that parses using read as an Int can also convert to an Int.</p> 389<pre><code>> instance DynamicCastable Int where 390> toOpaque = toOpaque . toInteger 391> fromOpaque o = fromInteger <$> fromOpaque o <|> (readMaybe =<< fromOpaque o)</code></pre> 392<p>And now</p> 393<p> <samp><kbd>dynamicCast (1 :: Int) :: Maybe Integer</kbd><br /> 394 Just 1</samp></p> 395<p> <samp><kbd>dynamicCast (1 :: Integer) :: Maybe Int</kbd><br /> 396 Just 1</samp></p> 397<p>This is pretty obvious and boring, but perhaps it gives us confidence that this is going to work at all. Let’s try something fancier.</p> 398<p>Void is the type with no inhabitants, so it can never be converted to.</p> 399<pre><code>> instance DynamicCastable Void where 400> fromOpaque _ = Nothing</code></pre> 401<p>Either is represented as just the item it contains, and any item can be contained in an Either.</p> 402<pre><code>> instance (DynamicCastable a, DynamicCastable b) => DynamicCastable (Either a b) where 403> toOpaque (Left x) = toOpaque x 404> toOpaque (Right x) = toOpaque x 405> 406> fromOpaque o = Left <$> fromOpaque o <|> Right <$> fromOpaque o</code></pre> 407<p>And now</p> 408<p> <samp><kbd>dynamicCast 1 :: Maybe (Either Int Void)</kbd><br /> 409 Just (Left 1)</samp></p> 410<p> <samp><kbd>dynamicCast 1 :: Maybe (Either Void Int)</kbd><br /> 411 Just (Right 1)</samp></p> 412<p> <samp><kbd>dynamicCast (Left 1 :: Either Int Void) :: Maybe Int</kbd><br /> 413 Just 1</samp></p> 414<p>Maybe is very similar, store the Just as the unwrapped value, and store Nothing as Void.</p> 415<pre><code>> instance (DynamicCastable a) => DynamicCastable (Maybe a) where 416> toOpaque (Just x) = toOpaque x 417> toOpaque Nothing = toOpaque (undefined :: Void) 418> 419> fromOpaque = fmap Just . fromOpaque</code></pre> 420<p> <samp><kbd>dynamicCast (Left 1 :: Either Int Void) :: Maybe (Maybe Int)</kbd><br /> 421 Just (Just 1)</samp></p> 422<p>To be able to cast the contents of a Functor, the possible failure also lives in the Functor, so we need a wrapper.</p> 423<pre><code>> newtype FunctorCast f a = FunctorCast (f (Maybe a)) 424 425> mkFunctorCast :: (Functor f) => f a -> FunctorCast f a 426> mkFunctorCast = FunctorCast . fmap Just 427 428> runFunctorCast :: FunctorCast f a -> f (Maybe a) 429> runFunctorCast (FunctorCast x) = x 430 431> runTraversableFunctorCast :: (Traversable f) => Maybe (FunctorCast f a) -> Maybe (f a) 432> runTraversableFunctorCast = join . fmap (sequenceA . runFunctorCast) 433 434> instance (Functor f, DynamicCastable a, Typeable f) => DynamicCastable (FunctorCast f a) where 435> toOpaque = Opaque . toDyn . fmap toOpaque . runFunctorCast 436> fromOpaque (Opaque dyn) = FunctorCast . fmap fromOpaque <$> fromDynamic dyn</code></pre> 437<p> <samp><kbd>runTraversableFunctorCast $ dynamicCast (mkFunctorCast ["1"]) :: Maybe [Either Void Integer]</kbd><br /> 438 Just [Right 1]</samp></p> 439 <p class="ccline"><a href="http://creativecommons.org/licenses/by/4.0/" rel="license">
439<img alt="Creative Commons Attribution 4.0 International License" src="http://i.creativecommons.org/l/by/4.0/88x31.png" /></a> © 2006-2026 Stephen Paul Weber. Some Rights Reserved.</p>]]></content:encoded> 440 <wfw:commentRss>https://singpolyma.net/2018/05/haskell2010-dynamic-cast/feed/</wfw:commentRss> 441 <slash:comments>4</slash:comments> 442 </item> 443 </channel> 444</rss>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.