summaryrefslogtreecommitdiffstats
path: root/index.xml
diff options
context:
space:
mode:
authorLoïc Hoguin <[email protected]>2017-01-22 13:51:38 +0100
committerLoïc Hoguin <[email protected]>2017-01-22 13:51:38 +0100
commit7bb98d0476dbe4ee5e9317e42ca17e4a7d717e0d (patch)
tree29b5fe0d72b65b9382d8b4e1d66a560a55557b77 /index.xml
parent1e5b5eb16cd528185246ad2b6cb356696ecafab6 (diff)
downloadninenines.eu-7bb98d0476dbe4ee5e9317e42ca17e4a7d717e0d.tar.gz
ninenines.eu-7bb98d0476dbe4ee5e9317e42ca17e4a7d717e0d.tar.bz2
ninenines.eu-7bb98d0476dbe4ee5e9317e42ca17e4a7d717e0d.zip
Publish the "Don't let it crash" article
Diffstat (limited to 'index.xml')
-rw-r--r--index.xml281
1 files changed, 122 insertions, 159 deletions
diff --git a/index.xml b/index.xml
index 6d4b70a1..31704d83 100644
--- a/index.xml
+++ b/index.xml
@@ -6,10 +6,131 @@
<description>Recent content on Nine Nines</description>
<generator>Hugo -- gohugo.io</generator>
<language>en-us</language>
- <lastBuildDate>Tue, 03 Jan 2017 00:00:00 +0100</lastBuildDate>
+ <lastBuildDate>Sun, 22 Jan 2017 00:00:00 +0100</lastBuildDate>
<atom:link href="https://ninenines.eu/index.xml" rel="self" type="application/rss+xml" />
<item>
+ <title>Don&#39;t let it crash</title>
+ <link>https://ninenines.eu/articles/dont-let-it-crash/</link>
+ <pubDate>Sun, 22 Jan 2017 00:00:00 +0100</pubDate>
+
+ <guid>https://ninenines.eu/articles/dont-let-it-crash/</guid>
+ <description>&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;We have a specific mindset when writing Erlang
+programs. We focus on the normal execution of the
+program and don&amp;#8217;t handle most of the errors that may
+occur. We sometimes call this normal execution the
+&lt;em&gt;happy path&lt;/em&gt;.&lt;/p&gt;&lt;/div&gt;
+&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;The general pattern behind writing only for the
+&lt;em&gt;happy path&lt;/em&gt;, letting the VM catch errors (writing
+them to a log for future consumption) and then
+having a supervisor restart the processes that
+failed from a clean state, has a name. We call it
+&lt;em&gt;let it crash&lt;/em&gt;; and it drives many of our design
+decisions.&lt;/p&gt;&lt;/div&gt;
+&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;It&amp;#8217;s a really great way to program and the results
+are fantastic compared to most other programming
+languages. And yet, &lt;em&gt;let it crash&lt;/em&gt; barely convinced
+anyone that they should use Erlang. Why would that
+be?&lt;/p&gt;&lt;/div&gt;
+&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;You may already know that Cowboy is capable of
+handling at least 2 million Websocket connections
+on a single server. This is in large part thanks
+to the capabilities of the VM. Still, 2 million
+is good, much better than most other servers can
+do.&lt;/p&gt;&lt;/div&gt;
+&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;Cowboy is not just a Websocket server; it&amp;#8217;s also
+an HTTP and HTTP/2 server, and it handles many
+related features like long polling or the parsing
+of most request headers.&lt;/p&gt;&lt;/div&gt;
+&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;Can you guess how large the Cowboy codebase is,
+without looking at the source?&lt;/p&gt;&lt;/div&gt;
+&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;Do make sure you have a clear answer in your mind
+before you go check.&lt;/p&gt;&lt;/div&gt;
+&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;Good, you are back. Now what were the results? If
+I am correct, you overestimated the size of Cowboy.
+Cowboy is in fact about five thousand lines of code.
+You probably thought it was at least ten thousand.
+About eighty percent of readers will have
+overestimated the size of Cowboy. And you did only
+because I mentioned it can handle millions of
+Websocket connections.&lt;/p&gt;&lt;/div&gt;
+&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;Numerous studies show this effect. Just mentioning
+the large number already prepared your mind to think
+in that direction. Repeating the number made you
+focus even more on it. Then the question asked for
+a number, which ended up larger than the reality.&lt;/p&gt;&lt;/div&gt;
+&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;The same effect can be applied to negotiation for
+example. You generally want to start by giving your
+offer (and not let the other party initiate) and
+you want to give a really large number first. You
+can also prepare your customer by mentioning an even
+larger number in the previous discussion.&lt;/p&gt;&lt;/div&gt;
+&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;And it&amp;#8217;s not just numbers either. An experiment
+showed that just by looking at an image of clouds,
+customers of a pillow store were buying pillows
+more comfortable (and more expensive) than those
+who didn&amp;#8217;t see that image.&lt;/p&gt;&lt;/div&gt;
+&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;This is the power of associations. It is covered in
+much larger detail in the books
+&lt;a href=&#34;https://www.amazon.com/Influence-Psychology-Persuasion-Robert-Cialdini/dp/006124189X&#34;&gt;Influence&lt;/a&gt;
+and
+&lt;a href=&#34;https://www.amazon.com/Pre-Suasion-Revolutionary-Way-Influence-Persuade/dp/1501109790&#34;&gt;Pre-suasion&lt;/a&gt;.
+I highly recommend reading those and applying what
+you learn to your daily life. I&amp;#8217;m definitely not
+a professional psychologist so take this post with
+a grain of salt.&lt;/p&gt;&lt;/div&gt;
+&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;When selling Erlang, whether we are selling it to
+a customer or trying to convince a developer friend
+to start using it, we often talk about how Erlang
+&lt;em&gt;lets you sleep at night&lt;/em&gt;, that it is auto healing
+and always gets fantastic uptimes.&lt;/p&gt;&lt;/div&gt;
+&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;And then we talk about &lt;em&gt;let it crash&lt;/em&gt;.&lt;/p&gt;&lt;/div&gt;
+&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;And we describe what it means.&lt;/p&gt;&lt;/div&gt;
+&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;We might as well just say that Erlang crashes a lot
+and then take the door. It would have the same effect.
+It doesn&amp;#8217;t even stop at programs crashing. You know
+what else crashes? Cars, planes, trains. Often with
+disastrous consequences. Is that really the message
+we want to convey?&lt;/p&gt;&lt;/div&gt;
+&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;They even &lt;a href=&#34;https://img.youtube.com/vi/oEUBW2lCkIk/0.jpg&#34;&gt;printed it on a t-shirt&lt;/a&gt;!
+Keep calm and let it crash. It&amp;#8217;s the kind of t-shirt
+you probably shouldn&amp;#8217;t wear in an airport, and for good
+reasons. A few people did, then realized what they were
+wearing and were not too smug about it.&lt;/p&gt;&lt;/div&gt;
+&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;And yet this is how we sell Erlang.&lt;/p&gt;&lt;/div&gt;
+&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;A better way would be to focus on the positives, of
+course, but also to make sure that those positives
+are phrased in a way that prevents bad associations
+to be formed in people&amp;#8217;s minds.&lt;/p&gt;&lt;/div&gt;
+&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;Instead of &lt;em&gt;let it crash&lt;/em&gt;, you can say that Erlang
+has &lt;em&gt;auto healing mechanisms&lt;/em&gt;. Healing is a good
+thing and accurately describes what happens in the
+system.&lt;/p&gt;&lt;/div&gt;
+&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;Should you need to go into more details, you will
+probably want to avoid &lt;em&gt;recover from crashes&lt;/em&gt; and
+instead say &lt;em&gt;recover from exceptions&lt;/em&gt;. Exceptions
+are a pretty neutral word and, should you explain
+what you mean by that, you can talk about exceptions
+that occur for reasons unrelated to Erlang, like
+hardware failure or network instability.&lt;/p&gt;&lt;/div&gt;
+&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;The trick is to always use positive words and
+phrases to describe Erlang, and to use external
+factors to explain how Erlang deals with failures.
+Never mention the failures internal to Erlang
+systems unless you are asked specifically, in
+which case you can say that the auto healing
+applies to all exceptions.&lt;/p&gt;&lt;/div&gt;
+&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;The &lt;em&gt;let it crash&lt;/em&gt; philosophy is great when
+learning Erlang or when writing fault-tolerant
+systems. But it&amp;#8217;s not going to convince anyone
+to use it unless they were already looking for
+it.&lt;/p&gt;&lt;/div&gt;
+&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;Do you like this post? Tell me on Twitter. I might
+make more.&lt;/p&gt;&lt;/div&gt;
+</description>
+ </item>
+
+ <item>
<title>Cowboy 2.0 pre-release 4</title>
<link>https://ninenines.eu/articles/cowboy-2.0.0-pre.4/</link>
<pubDate>Tue, 03 Jan 2017 00:00:00 +0100</pubDate>
@@ -1132,163 +1253,5 @@ project and make sure it doesn&amp;#8217;t happen again.&lt;/p&gt;&lt;/div&gt;
</description>
</item>
- <item>
- <title>Cowboy 2.0 and query strings</title>
- <link>https://ninenines.eu/articles/cowboy2-qs/</link>
- <pubDate>Wed, 20 Aug 2014 00:00:00 +0100</pubDate>
-
- <guid>https://ninenines.eu/articles/cowboy2-qs/</guid>
- <description>&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;Now that Cowboy 1.0 is out, I can spend some of my time thinking
-about Cowboy 2.0 that will be released soon after Erlang/OTP 18.0.
-This entry discusses the proposed changes to query string handling
-in Cowboy.&lt;/p&gt;&lt;/div&gt;
-&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;Cowboy 2.0 will respond to user wishes by simplifying the interface
-of the &lt;code&gt;cowboy_req&lt;/code&gt; module. Users want two things: less
-juggling with the Req variable, and more maps. Maps is the only
-dynamic key/value data structure in Erlang that we can match directly
-to extract values, allowing users to greatly simplify their code as
-they don&amp;#8217;t need to call functions to do everything anymore.&lt;/p&gt;&lt;/div&gt;
-&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;Query strings are a good candidate for maps. It&amp;#8217;s a list of
-key/values, so it&amp;#8217;s pretty obvious we can win a lot by using maps.
-However query strings have one difference with maps: they can have
-duplicate keys.&lt;/p&gt;&lt;/div&gt;
-&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;How are we expected to handle duplicate keys? There&amp;#8217;s no standard
-behavior. It&amp;#8217;s up to applications. And looking at what is done in
-the wild, there&amp;#8217;s no de facto standard either. While some ignore
-duplicate keys (keeping the first or the last they find), others
-require duplicate keys to end with &lt;code&gt;[]&lt;/code&gt; to automatically
-put the values in a list, or even worse, languages like PHP even
-allow you to do things like &lt;code&gt;key[something][other]&lt;/code&gt; and
-create a deep structure for it. Finally some allow any key to have
-duplicates and just gives you lists of key/values.&lt;/p&gt;&lt;/div&gt;
-&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;Cowboy so far had functions to retrieve query string values one
-value at a time, and if there were duplicates it would return the
-first it finds. It also has a function returning the entire list
-with all duplicates, allowing you to filter it to get all of them,
-and another function that returns the raw query string.&lt;/p&gt;&lt;/div&gt;
-&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;What are duplicates used for? Not that many things actually.&lt;/p&gt;&lt;/div&gt;
-&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;One use of duplicate keys is with HTML forms. It is common practice
-to give all related checkboxes the same name so you get a list of
-what&amp;#8217;s been checked. When nothing is checked, nothing is sent at all,
-the key is not in the list.&lt;/p&gt;&lt;/div&gt;
-&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;Another use of duplicate keys is when generating forms. A good
-example of that would be a form that allows uploading any number
-of files. When you add a file, client-side code adds another field
-to the form. Repeat up to a certain limit.&lt;/p&gt;&lt;/div&gt;
-&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;And that&amp;#8217;s about it. Of note is that HTML radio elements share
-the same name too, but only one key/value is sent, so they are not
-relevant here.&lt;/p&gt;&lt;/div&gt;
-&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;Normally this would be the part where I tell you how we solve
-this elegantly. But I had doubts. Why? Because there&amp;#8217;s no good
-solutions to solving only this particular problem.&lt;/p&gt;&lt;/div&gt;
-&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;I then stopped thinking about duplicate keys for a minute and
-started to think about the larger problem.&lt;/p&gt;&lt;/div&gt;
-&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;Query strings are input data. They take a particular form,
-and may be sent as part of the URI or as part of the request
-body. We have other kinds of input data. We have headers and
-cookies and the request body in various forms. We also have
-path segments in URIs.&lt;/p&gt;&lt;/div&gt;
-&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;What do you do with input data? Well you use it to do
-something. But there is one thing that you almost always do
-(and if you don&amp;#8217;t, you really should): you validate it and
-you map it into Erlang terms.&lt;/p&gt;&lt;/div&gt;
-&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;Cowboy left the user take care of validation and conversion
-into Erlang terms so far. Rather, it left the user take care
-of it everywhere except one place. Guess where? That&amp;#8217;s right,
-bindings.&lt;/p&gt;&lt;/div&gt;
-&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;If you define routes with bindings then you have the option
-to provide constraints. Constraints can be used to do two things:
-validate the data and convert it in a more appropriate term. For
-example if you use the &lt;code&gt;int&lt;/code&gt; constraint, Cowboy will
-make sure the binding is an integer, and will replace the value
-with the integer representation so that you can use it directly.
-In this particular case it not only routes the URI, but also
-validates and converts the bindings directly.&lt;/p&gt;&lt;/div&gt;
-&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;This is very relevant in the case of our duplicate keys,
-because if we have a list with duplicates of a key, chances
-are we want to convert that into a list of Erlang terms, and
-also make sure that all the elements in this list are expected.&lt;/p&gt;&lt;/div&gt;
-&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;The answer to this particular problem is simple. We need a
-function that will parse the query string and apply constraints.
-But this is not all, there is one other problem to be solved.&lt;/p&gt;&lt;/div&gt;
-&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;The other problem is that for the user some keys are mandatory
-and some are optional. Optional keys include the ones that
-correspond to HTML checkboxes: if the key for one or more
-checkbox is missing from the query string, we still want to
-have an empty list in our map so we can easily match. Matching
-maps is great, but not so much when values might be missing,
-so we have to normalize this data a little.&lt;/p&gt;&lt;/div&gt;
-&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;This problem is solved by allowing a default value. If the
-key is missing and a default exists, set it. If no default
-exists, then the key was mandatory and we want to crash.&lt;/p&gt;&lt;/div&gt;
-&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;I therefore make a proposal for changing the query string
-interface to three functions.&lt;/p&gt;&lt;/div&gt;
-&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;The first function already exists, it is &lt;code&gt;cowboy_req:qs(Req)&lt;/code&gt;
-and it returns only the query string binary. No more Req returned.&lt;/p&gt;&lt;/div&gt;
-&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;The second function is a renaming of &lt;code&gt;cowboy_req:qs_vals(Req)&lt;/code&gt;
-to something more explicit: &lt;code&gt;cowboy_req:parse_qs(Req)&lt;/code&gt;.
-The new name implies that a parsing operation is done. It was implicit
-and cached before. It will be explicit and not cached anymore now.
-Again, no more Req returned.&lt;/p&gt;&lt;/div&gt;
-&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;The third function is the one I mentioned above. I think
-the interface &lt;code&gt;cowboy_req:match_qs(Req, Fields)&lt;/code&gt; is
-most appropriate. It returns a normalized map that is the same
-regardless of optional fields being provided with the request,
-allowing for easy matching. It crashes if something went wrong.
-Still no Req returned.&lt;/p&gt;&lt;/div&gt;
-&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;I feel that this three function interface provides everything
-one would need to comfortably write applications. You can get
-low level and get the query string directly; you can get a list
-of key/value binaries without any additional processing and do it
-on your own; or you can get a processed map that contains Erlang
-terms ready to be used.&lt;/p&gt;&lt;/div&gt;
-&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;I strongly believe that by democratizing the constraints to
-more than just bindings, but also to query string, cookies and
-other key/values in Cowboy, we can allow the developer to quickly
-and easily go from HTTP request to Erlang function calls. The
-constraints are reusable functions that can serve as guards
-against unwanted data, providing convenience in the process.&lt;/p&gt;&lt;/div&gt;
-&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;Your handlers will not look like an endless series of calls
-to get and convert the input data, they will instead be just
-one call at the beginning followed by the actual application
-logic, thanks to constraints and maps.&lt;/p&gt;&lt;/div&gt;
-&lt;div class=&#34;listingblock&#34;&gt;
-&lt;div class=&#34;content&#34;&gt;&lt;!-- Generator: GNU source-highlight 3.1.8
-by Lorenzo Bettini
-http://www.lorenzobettini.it
-http://www.gnu.org/software/src-highlite --&gt;
-&lt;pre&gt;&lt;tt&gt;&lt;span style=&#34;font-weight: bold&#34;&gt;&lt;span style=&#34;color: #000000&#34;&gt;handle&lt;/span&gt;&lt;/span&gt;(&lt;span style=&#34;color: #009900&#34;&gt;Req&lt;/span&gt;, &lt;span style=&#34;color: #009900&#34;&gt;State&lt;/span&gt;) &lt;span style=&#34;color: #990000&#34;&gt;-&amp;gt;&lt;/span&gt;
- #{&lt;span style=&#34;color: #FF6600&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color: #990000&#34;&gt;:=&lt;/span&gt;&lt;span style=&#34;color: #009900&#34;&gt;Name&lt;/span&gt;, &lt;span style=&#34;color: #FF6600&#34;&gt;email&lt;/span&gt;&lt;span style=&#34;color: #990000&#34;&gt;:=&lt;/span&gt;&lt;span style=&#34;color: #009900&#34;&gt;Email&lt;/span&gt;, &lt;span style=&#34;color: #FF6600&#34;&gt;choices&lt;/span&gt;&lt;span style=&#34;color: #990000&#34;&gt;:=&lt;/span&gt;&lt;span style=&#34;color: #009900&#34;&gt;ChoicesList&lt;/span&gt;, &lt;span style=&#34;color: #FF6600&#34;&gt;remember_me&lt;/span&gt;&lt;span style=&#34;color: #990000&#34;&gt;:=&lt;/span&gt;&lt;span style=&#34;color: #009900&#34;&gt;RememberMe&lt;/span&gt;} &lt;span style=&#34;color: #990000&#34;&gt;=&lt;/span&gt;
- &lt;span style=&#34;font-weight: bold&#34;&gt;&lt;span style=&#34;color: #000000&#34;&gt;cowboy_req:match_qs&lt;/span&gt;&lt;/span&gt;(&lt;span style=&#34;color: #009900&#34;&gt;Req&lt;/span&gt;, [
- &lt;span style=&#34;font-weight: bold&#34;&gt;&lt;span style=&#34;color: #000080&#34;&gt;name&lt;/span&gt;&lt;/span&gt;, {&lt;span style=&#34;color: #FF6600&#34;&gt;email&lt;/span&gt;, &lt;span style=&#34;color: #FF6600&#34;&gt;email&lt;/span&gt;},
- {&lt;span style=&#34;color: #FF6600&#34;&gt;choices&lt;/span&gt;, &lt;span style=&#34;font-weight: bold&#34;&gt;&lt;span style=&#34;color: #0000FF&#34;&gt;fun&lt;/span&gt;&lt;/span&gt; &lt;span style=&#34;font-weight: bold&#34;&gt;&lt;span style=&#34;color: #000000&#34;&gt;check_choices&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;color: #990000&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color: #993399&#34;&gt;1&lt;/span&gt;, []},
- {&lt;span style=&#34;color: #FF6600&#34;&gt;remember_me&lt;/span&gt;, &lt;span style=&#34;color: #FF6600&#34;&gt;boolean&lt;/span&gt;, &lt;span style=&#34;color: #000080&#34;&gt;false&lt;/span&gt;}]),
- &lt;span style=&#34;font-weight: bold&#34;&gt;&lt;span style=&#34;color: #000000&#34;&gt;save_choices&lt;/span&gt;&lt;/span&gt;(&lt;span style=&#34;color: #009900&#34;&gt;Name&lt;/span&gt;, &lt;span style=&#34;color: #009900&#34;&gt;Email&lt;/span&gt;, &lt;span style=&#34;color: #009900&#34;&gt;ChoicesList&lt;/span&gt;),
- &lt;span style=&#34;font-weight: bold&#34;&gt;&lt;span style=&#34;color: #0000FF&#34;&gt;if&lt;/span&gt;&lt;/span&gt; &lt;span style=&#34;color: #009900&#34;&gt;RememberMe&lt;/span&gt; &lt;span style=&#34;color: #990000&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;font-weight: bold&#34;&gt;&lt;span style=&#34;color: #000000&#34;&gt;create_account&lt;/span&gt;&lt;/span&gt;(&lt;span style=&#34;color: #009900&#34;&gt;Name&lt;/span&gt;, &lt;span style=&#34;color: #009900&#34;&gt;Email&lt;/span&gt;); &lt;span style=&#34;color: #000080&#34;&gt;true&lt;/span&gt; &lt;span style=&#34;color: #990000&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color: #FF6600&#34;&gt;ok&lt;/span&gt; &lt;span style=&#34;font-weight: bold&#34;&gt;&lt;span style=&#34;color: #0000FF&#34;&gt;end&lt;/span&gt;&lt;/span&gt;,
- {&lt;span style=&#34;color: #FF6600&#34;&gt;ok&lt;/span&gt;, &lt;span style=&#34;color: #009900&#34;&gt;Req&lt;/span&gt;, &lt;span style=&#34;color: #009900&#34;&gt;State&lt;/span&gt;}&lt;span style=&#34;color: #990000&#34;&gt;.&lt;/span&gt;
-
-&lt;span style=&#34;font-weight: bold&#34;&gt;&lt;span style=&#34;color: #000000&#34;&gt;check_choices&lt;/span&gt;&lt;/span&gt;(&lt;span style=&#34;color: #990000&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color: #FF0000&#34;&gt;&#34;blue&#34;&lt;/span&gt;&lt;span style=&#34;color: #990000&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt;) &lt;span style=&#34;color: #990000&#34;&gt;-&amp;gt;&lt;/span&gt; {&lt;span style=&#34;color: #000080&#34;&gt;true&lt;/span&gt;, &lt;span style=&#34;color: #FF6600&#34;&gt;blue&lt;/span&gt;};
-&lt;span style=&#34;font-weight: bold&#34;&gt;&lt;span style=&#34;color: #000000&#34;&gt;check_choices&lt;/span&gt;&lt;/span&gt;(&lt;span style=&#34;color: #990000&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color: #FF0000&#34;&gt;&#34;red&#34;&lt;/span&gt;&lt;span style=&#34;color: #990000&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt;) &lt;span style=&#34;color: #990000&#34;&gt;-&amp;gt;&lt;/span&gt; {&lt;span style=&#34;color: #000080&#34;&gt;true&lt;/span&gt;, &lt;span style=&#34;color: #FF6600&#34;&gt;red&lt;/span&gt;};
-&lt;span style=&#34;font-weight: bold&#34;&gt;&lt;span style=&#34;color: #000000&#34;&gt;check_choices&lt;/span&gt;&lt;/span&gt;(&lt;span style=&#34;color: #990000&#34;&gt;_&lt;/span&gt;) &lt;span style=&#34;color: #990000&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color: #000080&#34;&gt;false&lt;/span&gt;;&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
-&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;(Don&amp;#8217;t look too closely at the structure yet.)&lt;/p&gt;&lt;/div&gt;
-&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;As you can see in the above snippet, it becomes really easy
-to go from query string to values. You can also use the map
-directly as it is guaranteed to only contain the keys you
-specified, any extra key is not returned.&lt;/p&gt;&lt;/div&gt;
-&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;This would I believe be a huge step up as we can now
-focus on writing applications instead of translating HTTP
-calls. Cowboy can now take care of it.&lt;/p&gt;&lt;/div&gt;
-&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;And to conclude, this also solves our duplicate keys
-dilemma, as they now automatically become a list of binaries,
-and this list is then checked against constraints that
-will fail if they were not expecting a list. And in the
-example above, it even converts the values to atoms for
-easier manipulation.&lt;/p&gt;&lt;/div&gt;
-&lt;div class=&#34;paragraph&#34;&gt;&lt;p&gt;As usual, feedback is more than welcome, and I apologize
-for the rocky structure of this post as it contains all the
-thoughts that went into this rather than just the conclusion.&lt;/p&gt;&lt;/div&gt;
-</description>
- </item>
-
</channel>
</rss> \ No newline at end of file