<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>Shinobu&#039;s Secrets</title>
	<atom:link href="http://zuttobenkyou.wordpress.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://zuttobenkyou.wordpress.com</link>
	<description>tips and thoughts</description>
	<lastBuildDate>Sat, 28 Jan 2012 09:45:36 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>
<cloud domain='zuttobenkyou.wordpress.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://s2.wp.com/i/buttonw-com.png</url>
		<title>Shinobu&#039;s Secrets</title>
		<link>http://zuttobenkyou.wordpress.com</link>
	</image>
	<atom:link rel="search" type="application/opensearchdescription+xml" href="http://zuttobenkyou.wordpress.com/osd.xml" title="Shinobu&#039;s Secrets" />
	<atom:link rel='hub' href='http://zuttobenkyou.wordpress.com/?pushpress=hub'/>
		<item>
		<title>Problems with the Portable Game Notation (PGN) Standard</title>
		<link>http://zuttobenkyou.wordpress.com/2012/01/27/problems-with-the-portable-game-notation-pgn-standard/</link>
		<comments>http://zuttobenkyou.wordpress.com/2012/01/27/problems-with-the-portable-game-notation-pgn-standard/#comments</comments>
		<pubDate>Fri, 27 Jan 2012 06:03:58 +0000</pubDate>
		<dc:creator>Shinobu</dc:creator>
				<category><![CDATA[Rant]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[chess]]></category>
		<category><![CDATA[parsing]]></category>
		<category><![CDATA[pgn]]></category>
		<category><![CDATA[standards]]></category>

		<guid isPermaLink="false">http://zuttobenkyou.wordpress.com/?p=942</guid>
		<description><![CDATA[I was trying to write a toy program to parse chess PGN (Portable Game Notation) files, to automate chess analysis using the free chess engine Stockfish. It turns out that I really, really hate PGN. Parsing PGN Requires a Legal Chess Move Generator This is the biggest problem with PGN. It uses SAN (Standard Algebraic [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=zuttobenkyou.wordpress.com&amp;blog=1384042&amp;post=942&amp;subd=zuttobenkyou&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>I was trying to write a toy program to parse chess PGN (Portable Game Notation) files, to automate chess analysis using the free chess engine Stockfish. It turns out that I really, really hate PGN.</p>
<h2>Parsing PGN Requires a Legal Chess Move Generator</h2>
<p>This is the biggest problem with PGN. It uses SAN (Standard Algebraic Notation, or just &#8220;algebraic notation&#8221;) to encode chess moves in a game. However, for almost inexplicable reasons, it decided to NEVER use disambiguation files/ranks whenever possible. For example, if there are two knights that can move to e1, but one of them is pinned by an enemy piece (moving this knight would expose the king to a check and is thus an illegal move), PGN states that the move must be encoded as &#8220;Ne1&#8243;.</p>
<p>Thanks to this, it is impossible to know what &#8220;Ne1&#8243; means any time you see it in PGN, unless you have a legal chess move generator to disambiguate it. The irony is that the disambiguation characters, though mentioned in the PGN standard (&#8220;8.2.3.4 Disambiguation&#8221;), do not help you disambiguate at all! If the standard just said &#8220;use <a href="https://www.chessclub.com/chessviewer/smith.html">Smith Notation</a>&#8220;, the requirement of any chess knowledge would not be necessary; each recorded move would be the only possible legal move anyway, and hence, would not require the use of a full-fledged move generator, and you&#8217;d only need to keep track of where the pieces are as you move along.</p>
<h2>Other Problems</h2>
<p>I&#8217;ve said 99% of what I wanted to say&#8230; But! There are still other problems with PGN that annoy me and probably every single other person out there who have tried to write a program to parse PGN data:</p>
<ul>
<li><strong>Two different formats</strong>: There are two PGN formats, with different stylistic requirements: &#8220;PGN Export&#8221; and &#8220;PGN Import&#8221; formats. I cannot find any reason why there should be two different formats. Just have 1 format, and call it &#8220;The PGN Standard, 2.0&#8243;!</li>
<li><strong>Stateful parsing</strong>: This is the result of the choice of SAN and disambiguation characters I mentioned above. You have to keep track of game state (where all the pieces are) in order to parse a sequence of moves. Technically speaking, even the use of Smith Notation requires that you keep track of where the pieces are, and hence, is stateful, so there is room for improvement here.</li>
<li><strong>It&#8217;s set in stone</strong>: The standard was last revised in 1994, nearly 20 years ago. Some parts of the standard remain undefined to this day.</li>
<li><strong>ASCII encoding</strong>: The world uses Unicode, and there ARE players whose names use non-English characters, such as European players, Russian players, Chinese players, etc.</li>
<li><strong>Confusion of human-friendliness and computer-friendliness</strong>: The use of SAN for the Movetext section was probably designed for easy legibility for humans. But there are other designs that make it extremely human-unfriendly, notably NAGs (Numeric Annotation Glyphs), which require that you use symbols like &#8220;$3&#8243; for the traditional &#8220;!!&#8221; move comment.</li>
<li><strong>80-character limit</strong>: The widespread &#8220;PGN Export&#8221; format requires you to limit every line to 80 characters. This is again due to confusion between human-friendliness and computer-friendliness. Computers would much rather have lines of text that are broken up <em>semantically</em> (e.g., pretty-printed XML or YAML). The case for human-friendliness is weak, because any better-than-Notepad text editor has sensible soft line-breaking. And no one encodes PGN by hand with a text editor anyway (everyone uses one of the many free PGN editing programs out there).</li>
<li><strong>255-character limit</strong>: The &#8220;PGN Import&#8221; format requires that a line must be less than 255 characters long.</li>
<li><strong>Messy use of newlines</strong>: This character is used to break up each line to 80 or 255 characters, and is hence can be ignored when parsing. Right? Wrong. It is also used to separate two PGN games from each other. Ugh!</li>
<li><strong>Confusion between content and style</strong>: Section &#8220;3.2.1: Byte equivalence&#8221; states, &#8220;For a given PGN data file, export format representations generated by different PGN programs on the same computing system should be exactly equivalent, byte for byte.&#8221; So, this makes even whitespace significant (an extra space character will violate the standard)&#8230; even though it doesn&#8217;t play a huge role (most of the time; see &#8220;Messy use of newlines&#8221; above).</li>
<li><strong>Seven Tag Roster</strong>: There are 7 tag pairs, or key-value pairs, that are mandatory for a game for &#8220;archival storage&#8221;. Here they are:
<ol>
<li>Event (the name of the tournament or match event)</li>
<li>Site (the location of the event)</li>
<li>Date (the starting date of the game)</li>
<li>Round (the playing round ordinal of the game)</li>
<li>White (the player of the white pieces)</li>
<li>Black (the player of the black pieces)</li>
<li>Result (the result of the game)</li>
</ol>
<p>        And, these tags must appear in the above order (again, confusion between content and style). Technically, these tags can have empty values, so from a parsing viewpoint, having these tags with empty values is the same thing as not having these tags at all. And some of these tags are very awkward for some situations (e.g., the &#8220;Site&#8221; tag, which is geographic in nature, doesn&#8217;t really apply for the case of correspondence chess.)
    </li>
<li><strong>Conflict between tag pairs and Movetext</strong>: The &#8220;PlyCount&#8221; tag, for example, is redundant and does nothing but introduce errors in a PGN file. (What would you, as programmer, trust &#8212; the PlyCount tag, or the actual Movetext section? So why have the &#8220;PlyCount&#8221; tag in the first place?) The &#8220;Result&#8221; tag is also redundant, because it is supposed to be present as a &#8220;Game Termination Marker&#8221; after the Movetext section. Yet another tag that you have to check for validity&#8230;</li>
<li><strong>Clumsy date tag</strong>: The &#8220;Date&#8221; tag uses a &#8220;YYYY.MM.DD&#8221; pattern to record the date. There is no way to disambiguate the order of games with the same opponent for those 5-minute blitz games that everyone plays on the internet.</li>
</ul>
<h2>A New, Sane Standard&#8230;</h2>
<p>If it were me, I&#8217;d do away with PGN entirely&#8230; there are just too many problems. Here are some ideas for a better chess game recording standard:</p>
<ul>
<li>Only require a list of moves. Do not require certain &#8220;tag pairs&#8221; such as the &#8220;Seven Tag Roster&#8221;. Make all additional data in addition to the list of actual game moves <em>optional</em>. Because, really, a game of chess is, essentially, a sequence of moves.</li>
<li>Use of &#8220;Full Notation&#8221; for recording moves. I hereby declare my support for a new notation, called &#8220;Full Notation&#8221;: each move records what piece is moving, what piece is captured (if any), what piece a pawn was promoted to (if any), whether it is a normal move, a castling move, or an en passant capture, and the starting and destination squares of the moving piece. For castling moves, it records the starting and destination squares for both the King and Rook (with maybe aliases &#8220;O-O&#8221; and &#8220;O-O-O&#8221; for traditional orthodox castling moves), for dead-simple clarity. This removes the need for a legal move generator when parsing the moves, and also gets rid of the need for disambiguation characters entirely. It also removes any need to keep track of where the pieces are. Simplicity always wins.</li>
<li>State which variant of chess we are playing, since there are many popular ones now, such as Chess960.</li>
<li>Design every feature to be computer-friendly, not human-friendly. Nobody writes raw PGN by hand from scratch, so there&#8217;s no concern for &#8220;alienating&#8221; any existing PGN users.</li>
<li>Maybe separate the actual game moves from the variations/commentary, to allow for easier basic parsing. The recursive structure used by PGN for defining move variations has been wildly successful (perhaps PGN&#8217;s only redeeming feature), and it makes sense to adopt this property. But I&#8217;m not sure what is the simplest way to represent the actual moves played vs. the variations/commentary.</li>
<li>Be very conservative against stylistic features (e.g., PGN&#8217;s Numeric Annotation Glyphs are an excellent example of what NOT to do).</li>
<li>Use XML or YAML (probably YAML). This would make it 10x easier to parse the game info, regardless of your programming-language-of-choice. This also automatically makes the standard computer-friendly.</li>
<li>Use Unicode.</li>
</ul>
<p>The above ideas would certainly make the format more &#8220;verbose&#8221; and require more disk space. But in the age of hard drives in the hundreds-of-gigabytes range, I think it makes a lot of sense to sacrifice the extra kilobytes per game to achieve simplicity. Besides, few people keep more than, say, 10,000 files in a plaintext format such as PGN. I would be delighted to see Scid, PyChess or any other free program adopt these ideas to create a new chess game recording standard&#8230; The most important feature I&#8217;d like to see would be the lack of the need for a legal move generator when parsing the moves. This alone would make the parsing 100x easier.</p>
<p>UPDATES:</p>
<ul>
<li><strong>January 27, 2012</strong>: Don&#8217;t suggest the use of long algebraic notation. Instead, support a new notation called &#8220;Full Notation&#8221;. Also clarify some points about keeping track of game state.</li>
</ul>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/zuttobenkyou.wordpress.com/942/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/zuttobenkyou.wordpress.com/942/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/zuttobenkyou.wordpress.com/942/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/zuttobenkyou.wordpress.com/942/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/zuttobenkyou.wordpress.com/942/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/zuttobenkyou.wordpress.com/942/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/zuttobenkyou.wordpress.com/942/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/zuttobenkyou.wordpress.com/942/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/zuttobenkyou.wordpress.com/942/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/zuttobenkyou.wordpress.com/942/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/zuttobenkyou.wordpress.com/942/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/zuttobenkyou.wordpress.com/942/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/zuttobenkyou.wordpress.com/942/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/zuttobenkyou.wordpress.com/942/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=zuttobenkyou.wordpress.com&amp;blog=1384042&amp;post=942&amp;subd=zuttobenkyou&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://zuttobenkyou.wordpress.com/2012/01/27/problems-with-the-portable-game-notation-pgn-standard/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">Shinobu</media:title>
		</media:content>
	</item>
		<item>
		<title>What do aliens look like?</title>
		<link>http://zuttobenkyou.wordpress.com/2012/01/18/what-do-aliens-look-like/</link>
		<comments>http://zuttobenkyou.wordpress.com/2012/01/18/what-do-aliens-look-like/#comments</comments>
		<pubDate>Wed, 18 Jan 2012 07:14:49 +0000</pubDate>
		<dc:creator>Shinobu</dc:creator>
				<category><![CDATA[Random]]></category>
		<category><![CDATA[Science]]></category>
		<category><![CDATA[aliens]]></category>
		<category><![CDATA[appearance]]></category>
		<category><![CDATA[humanoid]]></category>

		<guid isPermaLink="false">http://zuttobenkyou.wordpress.com/?p=938</guid>
		<description><![CDATA[Excuse the semi-random title, but, this question has been bugging me for a while. Ever since I was a kid and saw movies about space and aliens, I asked the question, &#8220;would real aliens really look like that?&#8221; Let&#8217;s face it, the mainstream film and art culture tend to portray aliens as humanoid life forms. [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=zuttobenkyou.wordpress.com&amp;blog=1384042&amp;post=938&amp;subd=zuttobenkyou&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Excuse the semi-random title, but, this question has been bugging me for a while. Ever since I was a kid and saw movies about space and aliens, I asked the question, &#8220;would real aliens really look like that?&#8221; Let&#8217;s face it, the mainstream film and art culture tend to portray aliens as humanoid life forms. How much of a humanoid form can be drawn from reasonable, educated guesses? And how much of it is just plain fantasy?</p>
<p>NOTE: I did not do any real research before writing this post, and am making educated guesses based off the top of my head. If you are an expert at any of the stuff (astrobiology in particular) please critique this post in the comments!</p>
<p>Before we examine what intelligent aliens could look like, let&#8217;s first look at the most basic conditions of where life can arise, if at all. There are some very intelligent guesses as to the cosmic ingredients of life. Let us examine each ingredient in turn.</p>
<h2>The habitable zone</h2>
<p>All life, as far as we know, have 1 function: change one form of energy into another. There must be a steady supply of energy that can be consumed. For us, it&#8217;s the Sun, mostly, although there is also life at the ocean depths that draw energy from thermal vents (and even these vents draw their energy from the heat of the Earth&#8217;s molten core, which is also due to radioactive decay). I think it&#8217;s safe to say that there must be either a good source of steady radiation for there to be life. Now, the best way to get a steady source of radioactive energy is by orbiting a star. Stars, if of the right size, have very good lifepsans (in the order of billions of years!). But orbiting a star also has another tremendous benefit: you get to stay at a stable location in space. If there was no Sun to orbit, the Earth would be hurling across space in some random direction (and it would be frozen over rather quickly).</p>
<p>So, there needs to be a sun-like star. The next thing necessary for life is probably a planet-sized blob of rock with some water and an atmosphere. The planet-scale size, water, and atmosphere really go hand-in-hand and can&#8217;t exist meaningfully without the other. Let us start with water. Water is necessary for life because it is the best chemically neutral &#8220;solvent&#8221; where many different chemical reactions can take place freely. Water also has the special property of being less dense as a solid than as a liquid, which keeps it from freezing over too easily; e.g., the icebergs of the Arctic float around, and melt, when they reach warmer waters &#8212; if the ice was to simply sink the moment it froze over, such melting processes would never occur. The rocky composition of the planet is required because it provides the only way to keep the water in a stable place (what we call oceans).</p>
<p>Now, water itself is a very precious substance &#8212; if you are designing a planet, you&#8217;d need a way to stop water from evaporating away into space. This is because the radioactive rays from the nearby sun could slowly &#8220;boil&#8221; the water, one molecule at a time, away. So, first, you&#8217;d need some sort of protective shield around the planet to keep the water safe. The Earth uses a magnetic field to do this, and thankfully the Earth is large enough to generate a strong enough magnetic field. This is the reason why our alien world would need to be planet-sized. Also, a planet can, by virtue of its size, retain most of the water with its gravitational field; if water molecules turn into separete Hydrogen and Oxygen gas moledules, their escape into outer space could be slowed down significantly, so that such evaporation would take many millions (or billions?) of years.</p>
<p>So we&#8217;ve established that you need water, and a planet to keep it. What about atmosphere? Well, the atmosphere plays a very important role on Earth: it shields us from too much radiation. Yes, the Earth&#8217;s magnetic field protects us from harmful radiation, but not all of it. You still get a lot of radiation from the sun itself. For us on Earth, the ozone layer plays a big role in protecting us from direct radiation. And so it is with our hypothetical alien world: it, too would need an atmosphere of some sort (and plus, if you have large bodies of water (oceans) with an atmosphere, you could get a water cycle, so that the water gets spread across into dry land, to allow for land-dwelling organisms).</p>
<h2>Land dweller</h2>
<p>Sun, rocky planet, water, and atmosphere. OK. Now comes I think probably the most controversial point: the <strong>intelligent</strong> alien would be a land-based animal. To support this hypothesis, I will first get rid of the other two alternatives: flying (winged) animals and marine (ocean-dwelling) animals. First, why can&#8217;t winged animals become intelligent? My guess is that intelligence evolved from the ability to manipulate nature in accurate, reproducible ways &#8212; i.e., we could create tools with our hands, and this separated the stupid and the less-stupid. Flying animals, by the laws of physics that govern our universe, cannot be too large. In fact, the smaller the better. This is why most flying organisms are insects. Also, because nature strives for brutal simplicity whenever it can, chances are that if you find a winged creature, it will only have wings, not wings and arms. And if you only have wings, then, you won&#8217;t be able to grab things in the accurate manner required to create tools.</p>
<p>Well, what about marine animals? Well, the problem is basically the same as that of winged animals: you will have fins, not arms or hands, because you need to swim. Dolphins of the future could have IQs that are 5000x higher than us, but without a means to manipulate nature around them with precision instruments (e.g., hands, opposable thumb, etc.), they will be forever doomed to the same patterns of behavior as their 50 IQ ancestors. If you think about it a marine animals are just like winged animals, except that their &#8220;wind&#8221; is the water. And as for land-dwelling marine animals, I also think that this category cannot produce any intelligent life, because of the following reason: the presence of water shortens the distance between all potential predators and prey. You need as big a distance as possible between you and the predator to survive as an intelligent life form, because this is the only way you can show Nature your intelligent decision-making skills and collect enough evolution points. If sharks around a 2-mile radius can sense your presence, and there are no trees to &#8220;climb up&#8221; to to avoid them, there is very, very little time to make any decisions. This is probably why the dominant evolutionary design of underwater land dwellers (other than fish) are crustaceans, with their natural armored shells. This short predator-prey distance, or PPD, explains why dolphins cannot truly sleep like we do.</p>
<h2>Sense organs and appearance</h2>
<p>So that leaves us with land-dwelling animals. We can take all the clues on our own planet to make some very good guesses in this final category. My only lament is that these guesses make the hypothetical intelligent alien very, very boring (and very humanoid). First, our alien must have limbs. All land-dwelling creatures on Earth have limbs, because limbs provide the best way to move about across a hard surface inside a low-density medium (the atmosphere). Let&#8217;s throw in hands here as well, because hands, as stated above, are the best natural tools to manipulate Nature in an accurate way. Since the two-hand, two-feet design is probably the best (and simplest) way to have hands and limbs for mobility, we&#8217;ll just adopt the &#8220;two hands, two feet&#8221; design. Next, the alien must have eyes. Eyes are one of the most primitive and basic organs (many bacteria have eyes), and provide the best bang-for-the-buck in terms of the information it can gather. Light is the fastest medium of information, and also the most prevalent (the alien Sun would provide a constant stream of light, as would any moons orbiting the planet). Light allowed our own human ancestors to maximize their PPD, because they could spot predators a mile away by just their color (this is impossible to do in the ocean), and it also allowed early hunter humans to communicate with abstract symbols to each other in a manner that only they, not the prey, could understand (what we now call hand signals) to maximize their intelligent decision making skills.</p>
<p>Hands, feet, and eyes. OK. What about a mouth, and nose? Well the mouth is certainly mandatory: how else can you eat food? There must be at least one opening for the nutrients to enter, and so there must be at least one mouth. As for the nose, for us humans it allows us to keep a well-salivated mouth, because we can breathe through our nose (which is dry by default); if we had to always breathe through our mouths, we would have a much harder time keeping our saliva flowing and ready to eat a meal when we chanced upon it. Our alien&#8217;s mouth would also have to be salivated with some chemicals to help it chew things and swallow the fine pieces as a single whole (imagine swallowing dry leaves with zero saliva&#8230; not pleasant). The nose also functions as a primitive poison detector (e.g., rotting food); if a species had to use their mouth to test something out every time they sat down for a meal, it would die out pretty fast.</p>
<p>The alien will also have ears, probably. This is because they allow the organism to sense things when the eyes can&#8217;t (e.g., at night, for example). Awareness of the environment is 99.9% of the evolution game, so anything to maximize information-gathering senses helps to keep the species from going extinct.</p>
<p>Eyes, mouth, nose, and ears. Amazing! Our alien probably has all of these things. Now comes an interesting question: does the alien have a face like we do? Chances are, I think, yes. First, the mouth has to go at the very bottom, because this is the only way of keeping the eyes above it. Why do the eyes need to placed above the mouth? It&#8217;s because of gravity: the alien, like us, we will pick up food from the ground (a freshly killed prey animal, any collection of food from multiple sources, etc.) more than from trees or other high places. Also, water will almost always be at ground-level. Having the eyes above the mouth allows us to look out for potential threats or opportunities while we eat, simultaneously. The alien nose will also probably find a spot between the eyes and mouth, because it can&#8217;t be below the mouth or above the eyes. If below the mouth, then it would simply get in the way when eating. The nose can&#8217;t be above the eyes because of gravity again: anything expelled from the nose would hinder the eyes. So the nose goes between the eyes and mouth. As for the eyes themselves, there would be at least two of them, because having two eyes provides highly accurate depth perception, and also acts as a primitive form of &#8220;insurance&#8221;: you can lose one eye but still go on your way. Lastly, the ears are located probably on the sides, on opposite poles, because that&#8217;s the best way to capture as many sound waves as possible from different directions. Since our alien has a &#8220;face&#8221;, the ears would have to go on a different axis &#8212; the simplest would be to have the ears on the left and right, not the front (face) and back.</p>
<p>Our hypothetical alien probably as a face and ears like ours. But does it also have a brain behind the face, on a &#8220;head&#8221;? Could it instead have a face and brain in the &#8220;torso&#8221;, if it has one? Well, I think the alien would also have a brain inside a &#8220;head&#8221; with a face, much like ours. Why? First, keeping the bulk of the sensory organs (eyes, nose, mouth, ears) in one place is great because it allows us to protect them from harmful threats in the most efficient way. If we had our sensory organs spread apart everywhere, it would be very difficult to protect them all at once. If we just cover our heads with our hands and arms, we can protect almost all of our important sensory organs quite effectively. By covering your face alone with one hand, you protect your eyes, nose, and mouth &#8212; impressive, don&#8217;t you think? Thus, the alien will also have a head for the face. Having a head provides another benefit: it allows maximum use of the other limbs and torso &#8212; you can put them underwater, inside mud, or whatever, and it has no detrimental effect on your main senses (eyes, ears, etc.).</p>
<p>Now, what about the brain? Is it also inside the head of the alien? Most likely, yes. This is because the center &#8220;torso&#8221; area of the alien will already be filled up with older, more primitive organs such as a heart, digestive organs, etc. And, chances are that the brain of its more stupid ancestors would have been very close to its major sensory organs such as its eyes, for reasons of simplicity (why have extra-long neurons that go from the eyes to some far-flung brain when you can have short neurons with a brain close by?).</p>
<h2>Conclusion</h2>
<p>My conclusion is that intelligent aliens will look very humanoid in form. And chances are, their Earth will look very similar to ours, with an atmosphere and oceans. I don&#8217;t think this prospect is boring at all. Just imagine &#8212; they too will have a word for &#8220;water&#8221;, and &#8220;Sun&#8221; and &#8220;Earth&#8221;, as well as &#8220;face&#8221;, &#8220;eyes&#8221;, etc. The more you think about it, the more fascinating it gets.</p>
<p>The only unpleasant part about it is that, if we ever do meet such aliens, they will freak us out with their warped humanlike appearance. But hey, at least the freakout will go both ways, right?</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/zuttobenkyou.wordpress.com/938/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/zuttobenkyou.wordpress.com/938/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/zuttobenkyou.wordpress.com/938/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/zuttobenkyou.wordpress.com/938/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/zuttobenkyou.wordpress.com/938/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/zuttobenkyou.wordpress.com/938/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/zuttobenkyou.wordpress.com/938/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/zuttobenkyou.wordpress.com/938/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/zuttobenkyou.wordpress.com/938/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/zuttobenkyou.wordpress.com/938/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/zuttobenkyou.wordpress.com/938/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/zuttobenkyou.wordpress.com/938/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/zuttobenkyou.wordpress.com/938/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/zuttobenkyou.wordpress.com/938/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=zuttobenkyou.wordpress.com&amp;blog=1384042&amp;post=938&amp;subd=zuttobenkyou&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://zuttobenkyou.wordpress.com/2012/01/18/what-do-aliens-look-like/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">Shinobu</media:title>
		</media:content>
	</item>
		<item>
		<title>Intro to QuickCheck and Hpc</title>
		<link>http://zuttobenkyou.wordpress.com/2011/12/29/intro-to-quickcheck-and-hpc/</link>
		<comments>http://zuttobenkyou.wordpress.com/2011/12/29/intro-to-quickcheck-and-hpc/#comments</comments>
		<pubDate>Thu, 29 Dec 2011 15:28:35 +0000</pubDate>
		<dc:creator>Shinobu</dc:creator>
				<category><![CDATA[Haskell]]></category>
		<category><![CDATA[guide]]></category>
		<category><![CDATA[haskell]]></category>
		<category><![CDATA[hpc]]></category>
		<category><![CDATA[intro]]></category>
		<category><![CDATA[quickcheck]]></category>
		<category><![CDATA[testing]]></category>
		<category><![CDATA[tutorial]]></category>

		<guid isPermaLink="false">http://zuttobenkyou.wordpress.com/?p=925</guid>
		<description><![CDATA[And you thought my Haskell honeymoon was over. So the other day, I finally got around to using the legendary QuickCheck testing library for a semi-serious project of mine. And I must say that I am very impressed with QuickCheck &#8212; it caught two subtle (but serious) errors in my core functions &#8212; in day [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=zuttobenkyou.wordpress.com&amp;blog=1384042&amp;post=925&amp;subd=zuttobenkyou&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>And you thought my Haskell honeymoon was over.</p>
<p>So the other day, I finally got around to using the legendary QuickCheck testing library for a semi-serious project of mine. And I must say that I am very impressed with QuickCheck &#8212; it caught two subtle (but serious) errors in my core functions &#8212; in day one! I&#8217;m going to explain some basic concepts to help you get started with QuickCheck immediately; afterwards I&#8217;ll throw in a brief discussion about the Haskell Program Coverage (&#8220;Hpc&#8221;) tool, too, since it goes well with QuickCheck.</p>
<h2>QuickCheck</h2>
<p>The point of QuickCheck is to generate tons of random, junk values (the nastier-looking, the better) to feed into your functions. Well, they&#8217;re not <em>really</em> junk values, from a type system perspective, but the point is to generate lots of edge-case values that we forget to consider when writing the code. QuickCheck supports testing of pure functions as well as monadic ones (ST monad), but I&#8217;ll just focus on the pure functions because that&#8217;s what I have experience with.</p>
<p>So the next question is, how does QuickCheck generate random values? Well, if your function uses any of the basic types like Int, [Integer], Double, Word64, etc., QuickCheck knows how to generate the random values already. But if you use a custom type, or a unique combination of ordinary types (e.g., (Maybe Int, Maybe Int)), you have to instruct QuickCheck how to generate it. You do this by writing an instance for QuickCheck&#8217;s <strong>Arbitrary</strong> typeclass.</p>
<p>So let&#8217;s say my custom type is (Maybe Int, Maybe Int), and that it has a special requirement: if it is (Just a, Just b), then <strong>b</strong> must be greater than <strong>a</strong>. No problem.</p>
<p><pre class="brush: plain;">
newtype MPair = MPair (Maybe Int, Maybe Int)
    deriving (Show)

instance Arbitrary MPair where
    arbitrary = do
        -- First, generate a random Int.
        a &lt;- arbitrary :: Gen Int
        -- Now generate another random Int, but such that it is greater than *a*.
        b &lt;- suchThat (arbitrary :: Gen Int) (&gt;a)
        -- Now choose between either (Just a), or Nothing.
        a' &lt;- elements [Just a, Nothing]
        -- Repeat the same coin flip for *b*.
        b' &lt;- elements [Just b, Nothing]
        -- Return the random result.
        return $ MPair (a', b')
</pre></p>
<p>There is a method to the madness! There are <a href="http://hackage.haskell.org/packages/archive/QuickCheck/latest/doc/html/Test-QuickCheck.html">other useful combinators</a> besides <strong>suchThat</strong> and <strong>elements</strong>, of course.</p>
<p>So now you&#8217;ve told QuickCheck how to generate the random values to stress-test your functions. The next step is to define the stress-tests. In the QuickCheck world, such testing functions are used to test very specific <em>properties</em> of your functions that <strong>must</strong> hold true no matter what you throw at it. By convention, these functions are named &#8220;prop_[function name]&#8220;.</p>
<p>So here&#8217;s an example, <strong>prop_isTouching</strong>, which, appropriately enough, tests the <strong>isTouching</strong> function..</p>
<p><pre class="brush: plain;">
prop_isTouching :: MPair -&gt; MPair -&gt; Bool
prop_isTouching x y =
    isTouching x' y' == isTouching y' x'
    where
    x' = fromMPair x
    y' = fromMPair y

isTouching :: (Ord a) =&gt; (Maybe a, Maybe a) -&gt; (Maybe a, Maybe a) -&gt; Bool
isTouching p@(pStart, pEnd) q@(qStart, qEnd)
    -- if no pair has both of its values defined, then just return False;
    -- technically this notion is incorrect, but the greater codebase behind this
    -- interprets it this way, so it's OK
    | not (isBoth p) || not (isBoth q) = False
    -- if one or both pairs are empty, then return False
    | isBlank p || isBlank q = False
    | isLeft q = inP qStart'
    | isRight q = inP qEnd'
    | isLeft p = inQ pStart'
    | isRight p = inQ pEnd'
    | otherwise = inP qStart'
        || inP qEnd'
        || inQ pStart'
        || inQ pEnd'
    where
    pStart' = fromJust pStart
    pEnd' = fromJust pEnd
    qStart' = fromJust qStart
    qEnd' = fromJust qEnd
    inP = isInside (pStart', pEnd')
    inQ = isInside (qStart', qEnd')

isLeft
    , isRight
    , isBoth
    , isBlank :: (Maybe a, Maybe a) -&gt; Bool
isLeft (a, b) = isJust a &amp;&amp; isNothing b
isRight (a, b) = isNothing a &amp;&amp; isJust b
isBoth (a, b) = isJust a &amp;&amp; isJust b
isBlank (a, b) = isNothing a &amp;&amp; isNothing b

fromMPair :: MPair -&gt; (Maybe Int, Maybe Int)
fromMPair (MPair a) = a

isInside :: (Ord a) =&gt; (a, a) -&gt; a -&gt; Bool
isInside (a, b) c = a &lt;= c &amp;&amp; c &lt;= b
</pre></p>
<p>You might be wondering, &#8220;Hey, I thought you were going to test a function that takes (Maybe Int, Maybe Int), not (Maybe a, Maybe a)!&#8221; Well, believe me, it pays off <em>a lot</em> to write higher-order functions like this that work on multiple types. The fact that we can test it using Maybe Ints (a very simple type) is just one benefit. Notice how I&#8217;ve made sure to restrict <strong>isTouching</strong>&#8216;s arguments to the Ord typeclass, since we expect the right hand value in the pair to be greater than the one on the left (if it exists). The fancy Arbitrary instance up above was not in vain.</p>
<p>Anyway, the <strong>isTouching</strong> function merely checks to see if one given pair &#8220;falls in&#8221; or &#8220;touches&#8221; the other pair of values. It&#8217;s a pretty mundane function, but such functions often form the backbone of the rest of your code, so it&#8217;s really important to get these 100% right. The various helper functions like isLeft, isRight, fromMPair, etc. may seem annoying, as if they get in the way of the example to test <strong>isTouching</strong> itself. But think about it: all of these auxiliary functions will, by virtue of their necessity, be tested by calling <strong>prop_isTouching</strong>! And if <strong>prop_isTouching</strong> keeps failing (and your custom type or function is too complex for a 10-minute debugging session), you can always add more prop_ functions to test these auxiliary functions in isolation. Haskell embraces small, cute functions, and so should you!</p>
<p>The <strong>prop_isTouching</strong> function itself is straightforward enough: it tests the &#8220;commutative&#8221; property of <strong>isTouching</strong> &#8212; that the order of the arguments does not matter.</p>
<p>So far so good. Now we just need to <em>run</em> <strong>prop_isTouching</strong> hundreds (or thousands) of times to see if it holds. QuickCheck defines some basic test running functions, for use in the IO monad. The simplest one is aptly named <strong>quickCheck</strong>. So, we can run the function above like so:</p>
<p><pre class="brush: plain;">
import Test.QuickCheck

import [Your testing module, where prop_isTouching resides]

main :: IO ()
main = quickCheck prop_isTouching
</pre></p>
<p>This will run prop_isTouching 100 times with the default testing settings. But default settings are usually not desirable (for me, I only caught errors when I &#8220;upped&#8221; the number of runs to 1000, among other things). Oh, and quickCheck will not tell you the name of the function it is testing. So here is a more useful version:</p>
<p><pre class="brush: plain;">
{-# LANGUAGE RecordWildCards #-}
import System.Exit
import Test.QuickCheck

import [Your testing module, where prop_isTouching resides]

-- Rigorous test arguments.
rigorous :: Args
rigorous = Args
    { replay = Nothing
    , maxSuccess = 1000 -- tests to run
    , maxDiscard = 1000 -- the number of tests that are thrown out and ignored b/c of &quot;==&gt;&quot; conditions, before &quot;giving up&quot; and failing due to too many discarded tests
    , maxSize = 1000 -- if a prop_ function uses a list ([]) type, maxSize is the max length of the randomly generated list
    , chatty = True
    }

-- Quick test arguments.
quick :: Args
quick = Args
    { replay = Nothing
    , maxSuccess = 100
    , maxDiscard = 100
    , maxSize = 100
    , chatty = True
    }

runTests :: [String] -&gt; IO ()
runTests as = case as of
    [] -&gt; runTests' quick
    a -&gt; case head a of
        &quot;1&quot; -&gt; runTests' quick
        &quot;2&quot; -&gt; runTests' rigorous
        _ -&gt; runTests' quick
    where
    runTests' :: Args -&gt; IO ()
    runTests' testArgs = do
        -- if all of your prop_ functions are of the same type, you can put
        -- them in a list and use mapM_ instead
        f prop_isTouching &quot;prop_isTouching&quot;
        f prop_someOtherFunc1 &quot;someOtherFunc1&quot;
        f prop_someOtherFunc2 &quot;someOtherFunc2&quot;
        f prop_someOtherFunc3 &quot;someOtherFunc3&quot;
        f prop_someOtherFunc4 &quot;someOtherFunc4&quot;
        where
        f prop str = do
            putStrLn str
            quitOnFail =&lt;&lt; quickCheckWithResult testArgs prop
        quitOnFail r = case r of
            -- pattern match with just two dots with RecordWildCards because I'm lazy
            Success{..} -&gt; return ()
            _ -&gt; exitFailure

main :: IO ()
main = getArgs &gt;&gt;= runTests
</pre></p>
<p>If you compile the above as &#8220;test&#8221;, then running &#8220;./test 2&#8243; will use the &#8220;rigorous&#8221; test settings. The key difference is that instead of <strong>quickCheck</strong>, we use <strong>quickCheckWithResult</strong>. With it, we can provide our choice of test settings (the <strong>Args</strong> type), and also get some feedback on what the test results were. For simplicity&#8217;s sake, we only check if the test was a complete success; if it&#8217;s anything else, we abort immediately.</p>
<p>Here&#8217;s a quick note about the term &#8220;shrink&#8221; that you might encounter: if QuickCheck spots a failure, it will first try to <em>shrink</em> the size of the random input repeatedly while maintaining the &#8220;failure&#8221; result (and QuickCheck will tell you about how many shrinks were performed). This is to help you work with reasonably small values (esp. useful if you have list arguments with hundreds of items each, like in <strong>prop_foo</strong> above).)</p>
<p>The careful reader would have wondered what the &#8220;==&gt;&#8221; was about in the comments. Well, the &#8220;==&gt;&#8221; function is called the &#8220;implication&#8221; function, and is used to throw out invalid values before running the <strong>prop_</strong> function. It&#8217;s another way to customize the random value, sort of like how we defined a custom Arbitrary instance up above for the MPair type synonym. For example,</p>
<p><pre class="brush: plain;">
prop_foo :: [Int] -&gt; [Int] -&gt; Prop
prop_foo xs ys =
    not (null xs) ==&gt; -- xs must not be null
    (length ys &gt; 3) ==&gt; -- ys must have at least 4 elements
    foo xs ys == foo ys xs
</pre></p>
<p>and it only gets to the &#8220;foo xs ys == bar xs ys&#8221; part if the two statements above are true. The only difference is that we have to end up with a <strong>Prop</strong> type instead of <strong>Bool</strong>, as was the case in <strong>prop_isTouching</strong>. No other part of your testing code needs to change. The advantage in using (==&gt;) is its ease of use &#8212; you can trivially write any rare edge-case condition that you know does not conform to the spec, without bothering to write an Arbitrary instance. However, the disadvantage is that QuickCheck will waste time generating invalid inputs before it gets to the test the function in question. Because of this, you should first try to create an instance of Arbitrary before going with (==&gt;).</p>
<p>Here is a version without using (==&gt;) just for illustrative purposes:</p>
<p><pre class="brush: plain;">
import Data.List
import Test.QuickCheck

main = verboseCheck prop_foo

newtype Xs = Xs [Int]
    deriving (Show)
newtype Ys = Ys [Int]
    deriving (Show)

prop_foo :: Xs -&gt; Ys -&gt; Bool
prop_foo (Xs xs) (Ys ys) =
    foo xs ys == foo ys xs

foo :: [Int] -&gt; [Int] -&gt; [Int]
foo xs ys = sort $ xs ++ ys

instance Arbitrary Xs where
    arbitrary = do
    -- xs must not be null
    xs &lt;- suchThat (arbitrary :: Gen [Int]) (not . null)
    return $ Xs xs

instance Arbitrary Ys where
    arbitrary = do
    -- ys must have at least 4 elements
    ys &lt;- suchThat (arbitrary :: Gen [Int]) ((&gt;3) . length)
    return $ Ys ys
</pre></p>
<p>That&#8217;s it! You now know enough to get started with QuickCheck <em>today</em>. No excuses! Make it a New Year&#8217;s resolution for 2012, if you have to!</p>
<p>QuickCheck has taught me to be skeptical of the &#8220;if it compiles, it is OK&#8221; Haskell attitude &#8212; it has really opened my eyes. I now want QuickCheck as a bare minimum testing setup for all of my Haskell code. No tests = poor design (<a href="http://en.wikipedia.org/wiki/Medieval_II:_Total_War#Patches">Medieval II: Total War</a>, I&#8217;m looking at you&#8230;).</p>
<p>QuickCheck can even be used to test C code with the Foreign Function Interface (FFI), so I&#8217;m forever tempted to go back to my other C projects and test them with QuickCheck as well (can you imagine the millions of bugs that lie hidden in C code everywhere &#8212; that could be exposed with just a little bit of Haskell knowledge and QuickCheck by the everyday developer? Suddenly, I am overcome with optimism&#8230; yet I fear that it is really pessimism underneath&#8230; hmm.)</p>
<h2>Hpc</h2>
<p>I said I would mention Hpc at the beginning. So, what does it do?</p>
<p>Simply put, Hpc tells you which codepaths were used (or not used) after running a binary executable. This is a <em>perfect</em> match for the binary you used for running QuickCheck tests, because during the course of its run QuickCheck will have fed tens of thousands of random values into your functions and have visited most, if not all, parts of your code. Thankfully, Hpc is included with GHC, so you already have it! Just compile your binary with the <strong>-fhpc</strong> flag. Here is the process:</p>
<ol>
<li>Compile your program with <strong>-fhpc</strong>. (ghc -fhpc &#8211;make foo.hs)</li>
<li>Run your program. (./foo)</li>
<li>Your program should have generated a <strong>*.tix</strong> metadata file, as well as a <strong>.hpc</strong> folder with <strong>*.mix</strong> files for hpc to analyze.</li>
<li>Run <strong>hpc report foo</strong> for a quick plaintext report on your codepaths.</li>
<li>Run <strong>hpc markup foo</strong> to generate prettified HTML files for a more comprehensive overview.</li>
<li>Rinse and repeat. Remove the old *.tix files if you don&#8217;t want combined results.</li>
</ol>
<p>One big caveat: make sure to delete any existing object files (*.o, *.hi) that you had earlier which were produced without the <strong>-fhpc</strong> flag! Otherwise, those portions of code will not generate hpc metadata! Alternatively, instead of deleting every *.o or *.hi file you have down your project&#8217;s folder tree, you could also pass the <strong>-fforce-recompile</strong> flag to <strong>ghc</strong> (this is the new name for the deprecated <strong>-no-recomp</strong> flag, which was used (sans explanation!) in Real World Haskell, Chapter 11).</p>
<p>Go forth, fellow Haskell newbies &#8212; embrace testing and code coverage analysis today!</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/zuttobenkyou.wordpress.com/925/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/zuttobenkyou.wordpress.com/925/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/zuttobenkyou.wordpress.com/925/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/zuttobenkyou.wordpress.com/925/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/zuttobenkyou.wordpress.com/925/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/zuttobenkyou.wordpress.com/925/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/zuttobenkyou.wordpress.com/925/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/zuttobenkyou.wordpress.com/925/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/zuttobenkyou.wordpress.com/925/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/zuttobenkyou.wordpress.com/925/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/zuttobenkyou.wordpress.com/925/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/zuttobenkyou.wordpress.com/925/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/zuttobenkyou.wordpress.com/925/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/zuttobenkyou.wordpress.com/925/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=zuttobenkyou.wordpress.com&amp;blog=1384042&amp;post=925&amp;subd=zuttobenkyou&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://zuttobenkyou.wordpress.com/2011/12/29/intro-to-quickcheck-and-hpc/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">Shinobu</media:title>
		</media:content>
	</item>
		<item>
		<title>Science and Religion (Creationism, etc.)</title>
		<link>http://zuttobenkyou.wordpress.com/2011/12/24/science-and-religion/</link>
		<comments>http://zuttobenkyou.wordpress.com/2011/12/24/science-and-religion/#comments</comments>
		<pubDate>Sat, 24 Dec 2011 22:26:15 +0000</pubDate>
		<dc:creator>Shinobu</dc:creator>
				<category><![CDATA[Random]]></category>
		<category><![CDATA[creationism]]></category>
		<category><![CDATA[philosophy]]></category>
		<category><![CDATA[religion]]></category>
		<category><![CDATA[science]]></category>

		<guid isPermaLink="false">http://zuttobenkyou.wordpress.com/?p=918</guid>
		<description><![CDATA[From what I understand, science seeks measurable truth. By &#8220;measurable&#8221; I mean something that is verifiable. Logical proofs are verifiable (as are mathematical proofs), and so are things we can touch, see, etc. Anything beyond measure is of no interest to scientists. There will always be a point where things become &#8220;unmeasurable&#8221; and &#8220;unknown.&#8221; It&#8217;s [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=zuttobenkyou.wordpress.com&amp;blog=1384042&amp;post=918&amp;subd=zuttobenkyou&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>From what I understand, science seeks measurable truth. By &#8220;measurable&#8221; I mean something that is verifiable. Logical proofs are verifiable (as are mathematical proofs), and so are things we can touch, see, etc. Anything beyond measure is of no interest to scientists.</p>
<p>There will always be a point where things become &#8220;unmeasurable&#8221; and &#8220;unknown.&#8221; It&#8217;s sort of like how you run out of answers to a 7-year old&#8217;s rigorous adherence to the Socratic method: if you keep asking <em>why</em> something is the way it is, you will eventually run out of answers. You will eventually run up against that boundary of measurable truth.</p>
<p>So this is the reason why I don&#8217;t understand why some people think that science can&#8217;t coexist with religion. Religion steps in exactly at the point where you cross into the unmeasurable truths. Religion answers questions like, &#8220;What happened before the Big Bang?&#8221; or &#8220;Why did the Big Bang occur?&#8221; where science cannot (at least currently). Science and religion, as far as I can see, are our eyes in the realms of measurable and unmeasurable truths, respectively. Interestingly, the study of philosophy sits right on the border, between what is measurable and not measurable, but what is still &#8220;true&#8221; in some sense of the word.</p>
<p>The problem with Creationism is that it tries (to comedic effect) to claim a huge chunk of science (evolution) as &#8220;unmeasurable.&#8221; It says that the human eye is so complicated yet so perfectly tuned that it must have had an intelligent creator behind its design. It says that the reason behind the existence of complex, inter-dependent organs is &#8220;unmeasurable&#8221; &#8212; unexplainable &#8212; by any scientific means. Of course, they have to blatantly ignore the mountains of evidence in support for the theory of evolution in doing so, as well as the beautiful <em>consistency</em> of it all.</p>
<p>I think the Creationists fear that the teaching of evolution will somehow destroy one&#8217;s belief in their Judeo-Christian God. But once you see that the realms of science and religion are *completely* separate, you quickly realize that such fear is unfounded. Rather, the Creationists should be concerned about the teaching of philosophy, as it asks questions that touch on religious teachings more directly.</p>
<p>Alas, unfortunately for the Christians, their Holy Book is embarrasingly wrong (this is the Word of God we are talking about!) on a lot of things because, like Creationism, it makes lots of utterly false statements that fall into what is measurable, into the domain of science. The story of Noah&#8217;s Ark is probably the best example. The &#8220;Earth is less than 10,000 years old&#8221; inference drawn from gathering the ages and lives of those described in the Bible is another one (it&#8217;s just plain wrong if you accept that fossils are real; and I&#8217;m not talking dinosaur bones &#8212; google &#8220;stromatolites&#8221; for some <em>really ancient</em> fossils). The whole thing about <em>miracles</em> is also problematic, because what was a miracle 2,000 years ago is not a miracle today. And for some reason all the miracles that happen today are limited to those that can be scientifically explained (but this is getting a bit off topic&#8230;).</p>
<p>Hmm, I guess teaching scientific knowledge in general will point to a lot of holes in the Bible, or any other Holy Book that dares to come under genuine scientific inquiry. Maybe the Bible should be rewritten. It could be that Noah&#8217;s Ark and the other accounts (some guy lived for 500+ years, IIRC) were just falsely written by some crazy guys in 100 BCE. You could just re-write the Bible and get all the good parts, like &#8220;love your enemies.&#8221; But this will never happen.</p>
<p>Indeed, the problem with pretty much all the world&#8217;s religions are that they have a &#8220;creation&#8221; story about how the Universe began, and end up with wonderfully confusing and comic descriptions that clash against modern scientific knowledge. I guess this is one reason why there has been an increasing rise in Atheism recently &#8212; the traditional religions have too many flaws!</p>
<p>Me, I just follow 1 simple code: &#8220;do good stuff, and don&#8217;t do bad stuff.&#8221; Simple, direct, and always correct. Plus, seeing the world this way, the vast majority of people I meet are also adherents of my code, at least from judging how they treat me. I don&#8217;t care enough about atheism or agnosticism to identify myself with one or the other.</p>
<p>EDIT: Grammar fix and clarify the title.<br />
EDIT January 3, 2012: Minor wording tweak.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/zuttobenkyou.wordpress.com/918/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/zuttobenkyou.wordpress.com/918/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/zuttobenkyou.wordpress.com/918/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/zuttobenkyou.wordpress.com/918/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/zuttobenkyou.wordpress.com/918/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/zuttobenkyou.wordpress.com/918/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/zuttobenkyou.wordpress.com/918/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/zuttobenkyou.wordpress.com/918/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/zuttobenkyou.wordpress.com/918/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/zuttobenkyou.wordpress.com/918/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/zuttobenkyou.wordpress.com/918/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/zuttobenkyou.wordpress.com/918/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/zuttobenkyou.wordpress.com/918/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/zuttobenkyou.wordpress.com/918/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=zuttobenkyou.wordpress.com&amp;blog=1384042&amp;post=918&amp;subd=zuttobenkyou&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://zuttobenkyou.wordpress.com/2011/12/24/science-and-religion/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">Shinobu</media:title>
		</media:content>
	</item>
		<item>
		<title>Simple Password Generation with Haskell</title>
		<link>http://zuttobenkyou.wordpress.com/2011/12/23/simple-password-generation-with-haskell/</link>
		<comments>http://zuttobenkyou.wordpress.com/2011/12/23/simple-password-generation-with-haskell/#comments</comments>
		<pubDate>Fri, 23 Dec 2011 04:40:37 +0000</pubDate>
		<dc:creator>Shinobu</dc:creator>
				<category><![CDATA[Haskell]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[gnupg]]></category>
		<category><![CDATA[haskell]]></category>
		<category><![CDATA[passwords]]></category>

		<guid isPermaLink="false">http://zuttobenkyou.wordpress.com/?p=909</guid>
		<description><![CDATA[So, I&#8217;ve been using a custom password generator for a while. It&#8217;s great because it has some useful settings like &#8220;only generate alphanumeric passwords&#8221; or &#8220;only generate letters&#8221; to deal with stupid, legacy-code websites that refuse to modernize their password handling code. I use this generator to create 50-character passwords, because hey, if it&#8217;s randomly [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=zuttobenkyou.wordpress.com&amp;blog=1384042&amp;post=909&amp;subd=zuttobenkyou&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>So, I&#8217;ve been using a custom password generator for a while. It&#8217;s great because it has some useful settings like &#8220;only generate alphanumeric passwords&#8221; or &#8220;only generate letters&#8221; to deal with stupid, legacy-code websites that refuse to modernize their password handling code. I use this generator to create 50-character passwords, because hey, if it&#8217;s randomly generated, then you might as well generate really long passwords! I recently upgraded the pseudorandom number generator (PRNG) to use a cryptographically secure PRNG (CPRNG), just for fun.</p>
<p>Anyway, here is the program:<br />
<pre class="brush: plain;">
-- LICENSE: PUBLIC DOMAIN
module Main where

import Control.Monad.State
import Crypto.Random.AESCtr
import Data.Binary (decode)
import qualified Data.ByteString.Lazy as B
import Data.List (nub)
import IO
import System (getArgs)
import System.IO (hSetEcho)

keysChar, keysNum, keysPunc, keysCharNum, keysAll, keysHex :: String
keysChar = ['a'..'z'] ++ ['A'..'Z']
keysHex = ['a'..'f']
keysNum = ['0'..'9']
keysPunc = &quot;`~!@#$%^&amp;*()-_=+[{]}\\|;:'\&quot;,&lt;.&gt;/? &quot;
keysCharNum = keysChar ++ keysNum
keysAll = keysChar ++ keysNum ++ keysPunc

giveKey ::  String -&gt; Char -&gt; Int -&gt; Char
giveKey keysCustom c n = extractChar $ case c of
    'i'  -&gt; (keysNum ++ keysHex)
    'j'  -&gt; keysNum
    'k'  -&gt; keysChar
    'l'  -&gt; keysCharNum
    ';'  -&gt; keysPunc
    'h'  -&gt; (keysCharNum ++ keysCustom)
    '\n' -&gt; ['\n']
    _    -&gt; keysAll
    where
    extractChar xs = xs!!mod n (length xs)

showRandomKey :: String -&gt; StateT AESRNG IO ()
showRandomKey keysCustom = handleKey =&lt;&lt; liftIO getChar
    where
    handleKey key = case key of
        '\n' -&gt; liftIO (putChar '\n') &gt;&gt; showRandomKey keysCustom
        'q' -&gt; (liftIO $ putStrLn &quot;\nBye!&quot;) &gt;&gt; return ()
        _ -&gt; mapM_ f [0..(49)::Int] &gt;&gt; (liftIO $ putStrLn []) &gt;&gt; showRandomKey keysCustom
        where
        f _ = liftIO
            . putChar
            . giveKey keysCustom key
            . (\n -&gt; mod n (length (keysAll ++ keysCustom) - 1))
            =&lt;&lt; aesRandomInt

aesRandomInt :: StateT AESRNG IO Int
aesRandomInt = do
    aesState &lt;- get
    let (bs, aesState') = genRandomBytes aesState 16
    put aesState'
    return (decode $ B.fromChunks [bs])

main :: IO ()
main = do
    hSetBuffering stdin NoBuffering -- disable buffering from STDIN
    hSetBuffering stdout NoBuffering -- disable buffering from STDOUT
    hSetEcho stdin False -- disable terminal echo
    as &lt;- getArgs
    let as' = filter (\c -&gt; elem c keysAll) . nub $ unwords as
    mapM_ putStrLn
        [ []
        , &quot;poke: 'q'     quit&quot;
        , &quot;      'j'     number&quot;
        , &quot;      'k'     letter&quot;
        , &quot;      'l'     alphanumeric&quot;
        , &quot;      ';'     punctuation&quot;
        , &quot;      'h'     alphanumeric&quot; ++ (if null as' then [] else &quot; + &quot; ++ as')
        , &quot;      'i'     hexadecimal&quot;
        , &quot;      'ENTER' newline&quot;
        , &quot;      else    any&quot;
        , []
        ]
    aesState &lt;- makeSystem -- gather entropy from the system to use as the initial seed
    _ &lt;- runStateT (showRandomKey as') aesState -- enter loop
    return ()
</pre></p>
<p>Yes, the CPRNG used is the Advanced Encryption Standard (AES) algorithm in counter mode. It sure beats using the default System.Random module in terms of&#8230; coolness (for the purposes of this application, the use of a CPRNG over a regular PRNG really gives no benefit). Anyway, here is a sample run:</p>
<p><pre class="brush: plain;">
$ poke \!@#$%
poke: 'q'     quit
      'j'     number
      'k'     letter
      'l'     alphanumeric
      ';'     punctuation
      'h'     alphanumeric + !@#$%
      'i'     hexadecimal
      'ENTER' newline
      else    any

%g@c@geisBaqK81ihYkEC5NyUrWXU2ndCKr3wHkklpjvCWF9I0
zaJ,!z{db.|~vR7,MvOnPU-5v7N;cCy'],bl/d;^s[hI}RM?j&gt;
KfdJKVygPSOufgllMAZlEaLWHSHpDrHIcgmryETcEsx5uUWlQb
49388c0958e9c51f2f4dc06a8097eb8169f715b4fcfb3ca555
</pre></p>
<p>I named it &#8220;poke&#8221; because I couldn&#8217;t think of any other short, UNIX-y name.</p>
<p>The program takes a string as input and treats it as a special class of characters to consider when generating a password with the &#8216;h&#8217; key. This way, you can fit into the requirements of legacy website code that say something like, &#8220;You may use alphanumeric characters and also &#8216;!&#8217; &#8216;@&#8217; &#8216;#&#8217; &#8216;$&#8217; and &#8216;%&#8217; symbols&#8221;.</p>
<p>The output above is from pressing &#8216;h&#8217;, then SPACE, then &#8216;l&#8217; and finally &#8216;i&#8217;. You could easily extend it to take more fine-tuned options, such as a shorter password length instead of the default 50. (Hint: use <a href="http://zuttobenkyou.wordpress.com/2011/04/19/haskell-using-cmdargs-single-and-multi-mode/" title="Haskell: Using CmdArgs (Single and Multi-Mode)">this post</a>.)</p>
<p>The interesting side effect of using 50-character long, randomly generated passwords is that I myself do not know these passwords! The only thing I remember is the single password used for my GnuPG private key, used to decrypt the master password file.</p>
<p>In case you are curious, my complete setup regarding passwords is as follows: I store all of my passwords in plaintext in a &#8220;master&#8221; file, then encrypt it with GnuPG. I use a git repo to track this encrypted file, so that I can sanely remove/delete old passwords without worrying about how to get it back if I need it. Once in a while, I use a simple shell command, <strong>gpg2 -d mypasswords.gpg | less</strong>, to view the passwords for entry into some website (hooray for copy and paste!). If I need to update/add/delete passwords, I just decrypt the master file, then edit it, and re-encrypt it and commit the change into git (btw, I decrypt it into a RAM partition to avoid leaving any traces of the plaintext file).</p>
<p>The GnuPG private key used to encrypt the master file is itself encrypted with CAST5 (again using GnuPG) and tracked in git. The CAST5 encryption (with the &#8220;-c&#8221; flag) is a way to encrypt a file with symmetric encryption &#8212; i.e., you supply the same password for encryption and decryption, and plus you don&#8217;t need a GnuPG key to do it! (You wouldn&#8217;t want to encrypt it using another GnuPG key, because then you&#8217;d need to encrypt the key for that GnuPG key as well &#8212; recursion!) I constantly sync the git repo holding these important, encrypted files across all of my personal machines, thumb drives, etc., for reliability and convenience.</p>
<p>I could learn to use KeyPass or some other software to manage my passwords, but, I&#8217;m too lazy. I&#8217;m also too paranoid to trust anyone other than myself to handle my passwords. I also take care not to store anything *too* valuable into the passwords file, or anything I type into any file anywhere, just to play it safe.</p>
<p>EDIT: Hey, this is my 100th post! Congratulations to me!</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/zuttobenkyou.wordpress.com/909/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/zuttobenkyou.wordpress.com/909/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/zuttobenkyou.wordpress.com/909/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/zuttobenkyou.wordpress.com/909/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/zuttobenkyou.wordpress.com/909/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/zuttobenkyou.wordpress.com/909/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/zuttobenkyou.wordpress.com/909/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/zuttobenkyou.wordpress.com/909/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/zuttobenkyou.wordpress.com/909/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/zuttobenkyou.wordpress.com/909/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/zuttobenkyou.wordpress.com/909/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/zuttobenkyou.wordpress.com/909/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/zuttobenkyou.wordpress.com/909/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/zuttobenkyou.wordpress.com/909/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=zuttobenkyou.wordpress.com&amp;blog=1384042&amp;post=909&amp;subd=zuttobenkyou&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://zuttobenkyou.wordpress.com/2011/12/23/simple-password-generation-with-haskell/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">Shinobu</media:title>
		</media:content>
	</item>
		<item>
		<title>Parsec Example Revisited (Again): Parsing Lazy ByteStrings</title>
		<link>http://zuttobenkyou.wordpress.com/2011/11/09/parsec-example-revisited-again-parsing-lazy-bytestrings/</link>
		<comments>http://zuttobenkyou.wordpress.com/2011/11/09/parsec-example-revisited-again-parsing-lazy-bytestrings/#comments</comments>
		<pubDate>Wed, 09 Nov 2011 19:53:04 +0000</pubDate>
		<dc:creator>Shinobu</dc:creator>
				<category><![CDATA[Haskell]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[bytestring]]></category>
		<category><![CDATA[haskell]]></category>
		<category><![CDATA[parsec]]></category>

		<guid isPermaLink="false">http://zuttobenkyou.wordpress.com/?p=891</guid>
		<description><![CDATA[So in this post, I said that I would parse ByteStrings next time just to show how easy it is. Well, I&#8217;ve done just that. Since most of the code is identical, I&#8217;m going to post a diff after the full code. And here is the diff: Like I said, pretty straightforward, right? The code [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=zuttobenkyou.wordpress.com&amp;blog=1384042&amp;post=891&amp;subd=zuttobenkyou&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>So in <a href="http://zuttobenkyou.wordpress.com/2011/11/07/parsec-example-revisited-custom-configuration-file-format-meets-the-token-module/" title="Parsec Example Revisited: Custom Configuration File Format Meets the Token Module">this</a> post, I said that I would parse ByteStrings next time just to show how easy it is. Well, I&#8217;ve done just that. Since most of the code is identical, I&#8217;m going to post a diff after the full code.</p>
<p><pre class="brush: plain; collapse: true; light: false; toolbar: true;">
-- LICENSE: PUBLIC DOMAIN
{-# LANGUAGE DeriveDataTypeable, RecordWildCards #-}
module Main where

import qualified Data.ByteString.Lazy as BL
import System.Console.CmdArgs.Implicit
import System.IO
import System.Environment
import System.Exit
import System.Process
import Text.Parsec.Char hiding (upper)
import Text.Parsec.Combinator
import Text.Parsec.Prim
import Text.Parsec.String
import qualified Text.Parsec.ByteString.Lazy as PB
import qualified Text.Parsec.Token as PT
import Text.Parsec.Language (emptyDef)
import Control.Monad.Identity

data Opts = Opts
    { all_devices :: Bool
    , unmount :: Bool
    , unmount_all :: Bool
    , discover :: Bool
    , no_color :: Bool
    } deriving (Data, Typeable, Show, Eq)

progOpts :: Opts
progOpts = Opts
    { all_devices = def &amp;= help &quot;mount all USB devices&quot;
    , unmount = def &amp;= help &quot;choose a USB device to unmount&quot;
    , unmount_all = def &amp;= name &quot;U&quot; &amp;= help &quot;unmount all USB devices&quot;
    , discover = def &amp;= help &quot;list all mounted/unmounted USB devices&quot;
    , no_color = def &amp;= help &quot;disable colors&quot;
    }
    &amp;= details
        [ &quot;Notes:&quot;
        , &quot;&quot;
        , &quot;The default behavior without any options is to try to mount a USB device.&quot;
            ++ &quot; Here, `device' means a device under the /dev directory, and in our context, is actually a file system partition.&quot;
            ++ &quot; Many USB drives have only a single partition, in which case the term `device' means both the USB drive and the single partition it has.&quot;
        , &quot;&quot;
        , &quot;Also, allowing the $USER to execute the mount and umount commands with sudo privileges (sudo visudo) will make things less clunky.&quot;
        ]

getOpts :: IO Opts
getOpts = cmdArgs $ progOpts
    &amp;= summary (_PROGRAM_INFO ++ &quot;, &quot; ++ _COPYRIGHT)
    &amp;= program _PROGRAM_NAME
    &amp;= help _PROGRAM_DESC
    &amp;= helpArg [explicit, name &quot;help&quot;, name &quot;h&quot;]
    &amp;= versionArg [explicit, name &quot;version&quot;, name &quot;v&quot;, summary _PROGRAM_INFO]

_PROGRAM_NAME, _PROGRAM_VERSION, _PROGRAM_INFO, _PROGRAM_DESC, _COPYRIGHT :: String
_PROGRAM_NAME = &quot;usbmnt&quot;
_PROGRAM_VERSION = &quot;0.1.0&quot;
_PROGRAM_INFO = _PROGRAM_NAME ++ &quot; version &quot; ++ _PROGRAM_VERSION
_PROGRAM_DESC = &quot;mount/unmount USB device(s)&quot;
_COPYRIGHT = &quot;(C) Linus Arver 2011&quot;

data BlockDevice = BlockDevice
    { shortname :: String
    , uuid :: UUID
    , fsys :: String
    , mountPoint :: MountPoint
    } deriving (Eq)

data MountPoint
    = MPath { path :: FilePath }
    | Swap
    | Unmounted
    | UnknownBlkidVal
    deriving (Eq)

instance Show BlockDevice where
    show BlockDevice{..} = unwords
        [ shortname
        , fsys
        , uuid
        , show mountPoint
        ]

instance Show MountPoint where
    show (MPath path) = path
    show Swap = &quot;Swap&quot;
    show Unmounted = &quot;Unmounted&quot;
    show UnknownBlkidVal = &quot;UnknownBlkidVal&quot;

blockdeviceDefault :: BlockDevice
blockdeviceDefault = BlockDevice
    { shortname = &quot;&quot;
    , uuid = &quot;&quot;
    , fsys = &quot;&quot;
    , mountPoint = MPath {path = &quot;&quot;}
    }

data Config = Config
    { fsyss :: [(String, String)]
    , uuids :: [(UUID, String)]
    } deriving (Eq, Show)

_ALPHANUM :: String
_ALPHANUM = ['a'..'z'] ++ ['A'..'Z'] ++ ['0'..'9']

data Color
    = Red
    | Green
    | Yellow
    | Blue
    | CNone
    deriving (Show, Eq)

colorize :: Color -&gt; String -&gt; String
colorize c s = case c of
    Blue -&gt; &quot;\x1b[1;34m&quot; ++ s ++ &quot;\x1b[0m&quot;
    Green -&gt; &quot;\x1b[1;32m&quot; ++ s ++ &quot;\x1b[0m&quot;
    Red -&gt; &quot;\x1b[1;31m&quot; ++ s ++ &quot;\x1b[0m&quot;
    Yellow -&gt; &quot;\x1b[1;33m&quot; ++ s ++ &quot;\x1b[0m&quot;
    _ -&gt; s

main :: IO ()
main = do
    hSetBuffering stdout NoBuffering
    hSetBuffering stderr NoBuffering
    opts &lt;- getOpts
    homeDir &lt;- getEnv &quot;HOME&quot;
    errNo &lt;- argsCheck opts homeDir
    when (errNo &gt; 0) $ exitWith $ ExitFailure errNo
    (devs, takenPaths) &lt;- getDevices opts
    let configLoc = homeDir ++ &quot;/.usbmnt&quot;
    configSrc &lt;- BL.readFile configLoc
    (confErrNo, config) &lt;- parseConfig configSrc configLoc
    when (confErrNo &gt; 0) $ exitWith $ ExitFailure confErrNo
    let mountablePaths = filter (\p -&gt; not $ elem p takenPaths) $ map (\p -&gt; &quot;/mnt/u&quot; ++ show p) [(0::Int)..]
        devsKV = zip (map show [(1::Int)..]) . zip devs $ mountablePaths
    prog opts config devsKV

argsCheck :: Opts -&gt; String -&gt; IO Int
argsCheck Opts{..} homeDir
    | null homeDir = e &quot;could not get environment variable $HOME&quot; 1
    | otherwise = return 0
    where
        e :: String -&gt; Int -&gt; IO Int
        e str num = errMsg str &gt;&gt; return num

prog :: Opts -&gt; Config -&gt; [(String, (BlockDevice, FilePath))] -&gt; IO ()
prog opts@Opts{..} config devsKV
    | discover = do
        putStrLn &quot;all devices:&quot;
        mapM_ (\(_, (d, _)) -&gt; putStrLn $ cshow d) devsKV
    | otherwise = do
        putStrLn (if (unmount || unmount_all)
            then &quot;USB device(s) to unmount:&quot;
            else &quot;USB device(s) to mount:&quot;)
        mapM_ (\(n, (d, _)) -&gt; putStrLn $ &quot;    &quot; ++ n ++ &quot;) &quot; ++ show' d) devsKV
        putStrLn &quot;&quot;
        mountMenu opts config devsKV
    where
        cshow :: BlockDevice -&gt; String
        cshow b@BlockDevice{..}
            | no_color = show b
            | otherwise = case mountPoint of
                Unmounted -&gt; colorize Green $ show b
                MPath _ -&gt; if not $ null $ getUSBMountPath b
                    then colorize Blue $ show b
                    else show b
                _ -&gt; show b
        show' :: BlockDevice -&gt; String
        show' = if not (unmount || unmount_all)
            then show
            else unwords . init . words . show

mountMenu :: Opts -&gt; Config -&gt; [(String, (BlockDevice, FilePath))] -&gt; IO ()
mountMenu Opts{..} config devsKV
    | unmount = if length devsKV == 1
        then do
            putStrLn &quot;only 1 USB device to unmount&quot;
            tryMount False config (snd . head $ devsKV) &gt;&gt;= exitWith
        else chooseDev prompt devsKV (tryMount False config)
    | unmount_all = do
        putStrLn &quot;unmounting all USB devices...&quot;
        mapM_ (tryMount False config) (map snd devsKV)
        return ()
    | all_devices = do
        putStrLn &quot;mounting all USB devices...&quot;
        mapM_ (tryMount True config) (map snd devsKV)
        return ()
    | length devsKV == 1 = do
        putStrLn &quot;only 1 USB device to mount&quot;
        tryMount True config (snd . head $ devsKV) &gt;&gt;= exitWith
    | otherwise = chooseDev prompt devsKV (tryMount True config)
    where
        prompt :: String
        prompt = if (unmount || unmount_all)
            then &quot;choose USB device to unmount (q to exit)&quot;
            else &quot;choose USB device to mount (q to exit)&quot;

chooseDev :: String -&gt; [(String, (BlockDevice, FilePath))] -&gt; ((BlockDevice, FilePath) -&gt; IO ExitCode) -&gt; IO ()
chooseDev prompt devsKV func = do
    putStrLn prompt
    key &lt;- getLine
    case lookup key devsKV of
        Just dev -&gt; func dev &gt;&gt;= exitWith
        _ -&gt; case key of
            &quot;q&quot; -&gt; return ()
            _ -&gt; chooseDev prompt devsKV func

tryMount :: Bool -&gt; Config -&gt; (BlockDevice, FilePath) -&gt; IO ExitCode
tryMount mount config@Config{..} (bd@BlockDevice{..}, mp)
    | (null margs) = do
        errMsg $ &quot;UUID &quot; ++ squote uuid ++ &quot; was not found in config file&quot;
        errMsg $ &quot;filesystem &quot; ++ squote fsys ++ &quot; was also not found in config file&quot;
        errMsg $ &quot;supported file systems: &quot; ++ (unwords $ map fst fsyss)
        exitWith (ExitFailure 1)
    | otherwise = do
    when mount $ do
        if (null $ mountArgsUUID config uuid)
            then putStrLn $ &quot;filesystem &quot; ++ squote fsys ++ &quot; found in config file&quot;
            else putStrLn $ &quot;UUID &quot; ++ squote uuid ++ &quot; found in config file&quot;
        putStrLn $ &quot;using these arguments: &quot; ++ squote margs
    putStr $ (if mount then &quot;&quot; else &quot;un&quot;)
        ++ &quot;mounting &quot;
        ++ shortname
        ++ &quot; (&quot; ++ fsys ++ &quot;) &quot;
        ++ (if mount then &quot;to &quot; ++ mp else &quot;from &quot; ++ show mountPoint)
        ++ &quot;..&quot;
    (_, _, _, p) &lt;- createProcess $ cmd margs shortname
    exitStatus &lt;- waitForProcess p
    if (exitStatus == ExitSuccess)
        then do
            putStrLn &quot;OK&quot;
            return ExitSuccess
        else do
            putStr &quot;FAILED\n&quot;
            errMsg $ (if mount
                then &quot;mount error (perhaps &quot; ++ squote mp ++ &quot; does not exist)&quot;
                else &quot;unmount error&quot;)
            return (ExitFailure 1)
    where
        margs = mountArgs config bd
        cmd arguments devPath = CreateProcess
            { cmdspec = ShellCommand (if mount
                then &quot;sudo mount -t &quot; ++ arguments ++ &quot; &quot; ++ devPath ++ &quot; &quot; ++ mp ++ &quot; &amp;&gt;/dev/null&quot;
                else &quot;sudo umount &quot; ++ show mountPoint)
            , cwd = Nothing
            , env = Nothing
            , std_in = CreatePipe
            , std_out = CreatePipe
            , std_err = Inherit
            , close_fds = False
            }

mountArgs :: Config -&gt; BlockDevice -&gt; String
mountArgs Config{..} BlockDevice{..} = case lookup uuid uuids of
    Just a -&gt; a
    _ -&gt; case lookup fsys fsyss of
        Just a -&gt; a
        _ -&gt; []

mountArgsUUID :: Config -&gt; UUID -&gt; String
mountArgsUUID Config{..} uuid' = case lookup uuid' uuids of
    Just a -&gt; a
    _ -&gt; []

getDevices :: Opts -&gt; IO ([BlockDevice], [String])
getDevices Opts{..} = do
    (_, sout, _, p) &lt;- createProcess cmdBlkid
    devs &lt;- case sout of
        Just h -&gt; hGetContents h
        Nothing -&gt; return []
    _ &lt;- waitForProcess p
    let devs' = (map (unwords . words)) . drop 2 . lines $ devs
    devs'' &lt;- mapM parseBlkid devs'
    let toMount = filter (\BlockDevice{..} -&gt; mountPoint == Unmounted) devs''
        toUnmount = filter (\dev -&gt; not $ null $ getUSBMountPath dev) devs''
        takenPaths = filter (not . null) . map getUSBMountPath $ devs''
    when (not discover &amp;&amp; null toMount &amp;&amp; (not (unmount || unmount_all))) $ do
        errMsg $ &quot;cannot find USB devices to mount&quot;
        exitWith (ExitFailure 1)
    when (not discover &amp;&amp; null toUnmount &amp;&amp; (unmount || unmount_all)) $ do
        errMsg $ &quot;cannot find USB devices to unmount&quot;
        exitWith (ExitFailure 1)
    return $ formatDevs devs'' toMount toUnmount takenPaths
    where
        formatDevs :: [BlockDevice] -&gt; [BlockDevice] -&gt; [BlockDevice] -&gt; [String] -&gt; ([BlockDevice], [String])
        formatDevs ds m um takenPaths
            | discover = (ds, takenPaths)
            | unmount || unmount_all = (um, takenPaths)
            | otherwise = (m, takenPaths)
        cmdBlkid = CreateProcess
            { cmdspec = ShellCommand (&quot;sudo blkid -o list&quot;)
            , cwd = Nothing
            , env = Nothing
            , std_in = CreatePipe
            , std_out = CreatePipe
            , std_err = Inherit
            , close_fds = False
            }

getUSBMountPath :: BlockDevice -&gt; String
getUSBMountPath BlockDevice{..} = case mountPoint of
    MPath str -&gt; if take 6 str == &quot;/mnt/u&quot; &amp;&amp; (all (\c -&gt; elem c ['0'..'9']) (drop 6 str))
        then str
        else &quot;&quot;
    _ -&gt; &quot;&quot;

errMsg :: String -&gt; IO ()
errMsg msg = hPutStrLn stderr $ &quot;error: &quot; ++ msg

squote :: String -&gt; String
squote s = &quot;`&quot; ++ s ++ &quot;'&quot;

-- Parsing

-- for parsing the computer-generated output of `sudo blkid -o list'
parserIdentifier :: Parser String
parserIdentifier = many1 $ oneOf $ _ALPHANUM ++ &quot;/-_&quot;

parserWhitespace :: Parser String
parserWhitespace = many1 $ oneOf &quot; \t\n\r&quot;

parserMP :: Parser MountPoint
parserMP =
    try ( do
        a &lt;- oneOf &quot;&lt;(&quot;
        b &lt;- manyTill anyChar (lookAhead $ (oneOf &quot;&gt;)&quot;))
        _ &lt;- oneOf &quot;&gt;)&quot;
        let mp = case a of
                '&lt;' -&gt; Swap
                '(' -&gt; case b of
                    &quot;not mounted&quot; -&gt; Unmounted
                    _ -&gt; UnknownBlkidVal
                _ -&gt; UnknownBlkidVal
        return mp
        )
    &lt;|&gt; (parserIdentifier &gt;&gt;= (\s -&gt; return MPath {path = s}))
    &lt;?&gt; &quot;blkid's mount point description&quot;

blkidParser :: Parser BlockDevice
blkidParser =
    try ( do
        sname &lt;- parserIdentifier
        _ &lt;- parserWhitespace
        fs &lt;- parserIdentifier
        _ &lt;- parserWhitespace
        _ &lt;- parserIdentifier -- leave out the &quot;label&quot; column, even if it exists
        _ &lt;- parserWhitespace
        mp &lt;- parserMP
        _ &lt;- parserWhitespace
        uid &lt;- parserIdentifier
        eof
        return BlockDevice
           { shortname = sname
           , uuid = uid
           , fsys = fs
           , mountPoint = mp
           }
        )
    &lt;|&gt;
    do  sname &lt;- parserIdentifier
        _ &lt;- parserWhitespace
        fs &lt;- parserIdentifier
        _ &lt;- parserWhitespace
        mp &lt;- parserMP
        _ &lt;- parserWhitespace
        uid &lt;- parserIdentifier
        eof
        return BlockDevice
            { shortname = sname
            , uuid = uid
            , fsys = fs
            , mountPoint = mp
            }
    &lt;?&gt; &quot;5 or 4 fields to parse&quot;

parseBlkid :: String -&gt; IO BlockDevice
parseBlkid src =
    case parse blkidParser &quot;output of `sudo blkid -o list'&quot; src of
        Left parseError -&gt; errMsg (show parseError) &gt;&gt; return blockdeviceDefault
        Right result -&gt; return result

-- we use a LanguageDef so that we can get whitespace/newline parsing for FREE
-- in our .usbmnt file
configDef :: PT.GenLanguageDef BL.ByteString () Identity
configDef = emptyDef
    { PT.commentStart   = &quot;&quot;
    , PT.commentEnd     = &quot;&quot;
    , PT.commentLine    = &quot;#&quot;
    , PT.nestedComments = False
    -- the identStart/identLetter define what a UUID will look like (a
    -- dash-separated hex number)
    , PT.identStart     = oneOf $ ['0'..'9'] ++ ['a'..'f'] ++ ['A'..'F']
    , PT.identLetter    = oneOf $ ['0'..'9'] ++ ['a'..'f'] ++ ['A'..'F'] ++ &quot;-&quot;
    , PT.opStart        = char '.'
    , PT.opLetter       = char '.'
    , PT.reservedOpNames= []
    , PT.reservedNames  = []
    , PT.caseSensitive  = True
    }

-- we call makeTokenParser def and pick out just those we need
lexer :: PT.GenTokenParser BL.ByteString () Identity
lexer = PT.makeTokenParser configDef

p_identifier :: ParsecT BL.ByteString () Identity String
p_identifier = PT.identifier lexer
p_stringLiteral :: ParsecT BL.ByteString () Identity String
p_stringLiteral = PT.stringLiteral lexer
p_whiteSpace :: ParsecT BL.ByteString () Identity ()
p_whiteSpace = PT.whiteSpace lexer
p_braces :: ParsecT BL.ByteString () Identity a -&gt; ParsecT BL.ByteString () Identity a
p_braces = PT.braces lexer
p_commaSep :: ParsecT BL.ByteString () Identity a -&gt; ParsecT BL.ByteString () Identity [a]
p_commaSep = PT.commaSep lexer
p_symbol :: String -&gt; ParsecT BL.ByteString () Identity String
p_symbol = PT.symbol lexer

type UUID = String

assocParser :: PB.Parser String -&gt; PB.Parser (UUID, String)
assocParser keyParser = do
    key &lt;- keyParser
    _ &lt;- many $ oneOf &quot; \t&quot;
    _ &lt;- string &quot;=&quot;
    _ &lt;- many $ oneOf &quot; \t&quot;
    mountOpts &lt;- p_stringLiteral
    return (key, mountOpts)
    &lt;?&gt; &quot;a key-value association&quot;

hashParser :: String -&gt; PB.Parser String -&gt; PB.Parser [(String, String)]
hashParser hashName keyParser = do
    _ &lt;- p_symbol hashName
    _ &lt;- p_symbol &quot;=&quot;
    a &lt;- p_braces (p_commaSep $ assocParser keyParser)
    return a
    &lt;?&gt; &quot;a &quot; ++ hashName ++ &quot; curly brace block&quot;

configParser :: PB.Parser Config
configParser = do
    p_whiteSpace -- take care of leading whitespace/comments as defined by configDef
    -- parse FSYS_HASH first
    fsyss' &lt;- hashParser &quot;FSYS_HASH&quot; (many1 alphaNum)
    p_whiteSpace
    -- now parse UUID_HASH
    uuids' &lt;- hashParser &quot;UUID_HASH&quot; (p_identifier)
    eof
    return $ Config {fsyss = fsyss', uuids = uuids'}
    &lt;?&gt; &quot;config with FSYS_HASH and UUID_HASH blocks&quot;

parseConfig :: BL.ByteString -&gt; String -&gt; IO (Int, Config)
parseConfig src loc =
    case parse configParser (&quot;config file at &quot; ++ squote loc) src of
        Left parseError -&gt; errMsg (show parseError) &gt;&gt; return (1, Config [] [])
        Right result -&gt; return (0, result)
</pre></p>
<p>And here is the diff:</p>
<p><pre class="brush: plain; collapse: true; light: false; toolbar: true;">
--- usbmnt.hsold	2011-11-09 11:43:02.871554967 -0800
+++ usbmnt.hsnew	2011-11-09 11:43:09.761453413 -0800
@@ -2,6 +2,7 @@
 {-# LANGUAGE DeriveDataTypeable, RecordWildCards #-}
 module Main where
 
+import qualified Data.ByteString.Lazy as BL
 import System.Console.CmdArgs.Implicit
 import System.IO
 import System.Environment
@@ -11,6 +12,7 @@
 import Text.Parsec.Combinator
 import Text.Parsec.Prim
 import Text.Parsec.String
+import qualified Text.Parsec.ByteString.Lazy as PB
 import qualified Text.Parsec.Token as PT
 import Text.Parsec.Language (emptyDef)
 import Control.Monad.Identity
@@ -126,7 +128,7 @@
     when (errNo &gt; 0) $ exitWith $ ExitFailure errNo
     (devs, takenPaths) &lt;- getDevices opts
     let configLoc = homeDir ++ &quot;/.usbmnt&quot;
-    configSrc &lt;- readFile configLoc
+    configSrc &lt;- BL.readFile configLoc
     (confErrNo, config) &lt;- parseConfig configSrc configLoc
     when (confErrNo &gt; 0) $ exitWith $ ExitFailure confErrNo
     let mountablePaths = filter (\p -&gt; not $ elem p takenPaths) $ map (\p -&gt; &quot;/mnt/u&quot; ++ show p) [(0::Int)..]
@@ -379,7 +381,7 @@
 
 -- we use a LanguageDef so that we can get whitespace/newline parsing for FREE
 -- in our .usbmnt file
-configDef :: PT.LanguageDef st
+configDef :: PT.GenLanguageDef BL.ByteString () Identity
 configDef = emptyDef
     { PT.commentStart   = &quot;&quot;
     , PT.commentEnd     = &quot;&quot;
@@ -397,25 +399,25 @@
     }
 
 -- we call makeTokenParser def and pick out just those we need
-lexer :: PT.TokenParser ()
+lexer :: PT.GenTokenParser BL.ByteString () Identity
 lexer = PT.makeTokenParser configDef
 
-p_identifier :: ParsecT String () Identity String
+p_identifier :: ParsecT BL.ByteString () Identity String
 p_identifier = PT.identifier lexer
-p_stringLiteral :: ParsecT String () Identity String
+p_stringLiteral :: ParsecT BL.ByteString () Identity String
 p_stringLiteral = PT.stringLiteral lexer
-p_whiteSpace :: ParsecT String () Identity ()
+p_whiteSpace :: ParsecT BL.ByteString () Identity ()
 p_whiteSpace = PT.whiteSpace lexer
-p_braces :: ParsecT String () Identity a -&gt; ParsecT String () Identity a
+p_braces :: ParsecT BL.ByteString () Identity a -&gt; ParsecT BL.ByteString () Identity a
 p_braces = PT.braces lexer
-p_commaSep :: ParsecT String () Identity a -&gt; ParsecT String () Identity [a]
+p_commaSep :: ParsecT BL.ByteString () Identity a -&gt; ParsecT BL.ByteString () Identity [a]
 p_commaSep = PT.commaSep lexer
-p_symbol :: String -&gt; ParsecT String () Identity String
+p_symbol :: String -&gt; ParsecT BL.ByteString () Identity String
 p_symbol = PT.symbol lexer
 
 type UUID = String
 
-assocParser :: Parser String -&gt; Parser (UUID, String)
+assocParser :: PB.Parser String -&gt; PB.Parser (UUID, String)
 assocParser keyParser = do
     key &lt;- keyParser
     _ &lt;- many $ oneOf &quot; \t&quot;
@@ -425,7 +427,7 @@
     return (key, mountOpts)
     &lt;?&gt; &quot;a key-value association&quot;
 
-hashParser :: String -&gt; Parser String -&gt; Parser [(String, String)]
+hashParser :: String -&gt; PB.Parser String -&gt; PB.Parser [(String, String)]
 hashParser hashName keyParser = do
     _ &lt;- p_symbol hashName
     _ &lt;- p_symbol &quot;=&quot;
@@ -433,7 +435,7 @@
     return a
     &lt;?&gt; &quot;a &quot; ++ hashName ++ &quot; curly brace block&quot;
 
-configParser :: Parser Config
+configParser :: PB.Parser Config
 configParser = do
     p_whiteSpace -- take care of leading whitespace/comments as defined by configDef
     -- parse FSYS_HASH first
@@ -445,7 +447,7 @@
     return $ Config {fsyss = fsyss', uuids = uuids'}
     &lt;?&gt; &quot;config with FSYS_HASH and UUID_HASH blocks&quot;
 
-parseConfig :: String -&gt; String -&gt; IO (Int, Config)
+parseConfig :: BL.ByteString -&gt; String -&gt; IO (Int, Config)
 parseConfig src loc =
     case parse configParser (&quot;config file at &quot; ++ squote loc) src of
         Left parseError -&gt; errMsg (show parseError) &gt;&gt; return (1, Config [] [])
</pre></p>
<p>Like I said, pretty straightforward, right? The code actually only uses lazy bytestrings for parsing the configuration file. The output of the <strong>blkid</strong> command is still parsed with the old way (native Haskell strings). The only tricky part is to import the bytestring stuff (Data.ByteString.Lazy and Text.Parsec.ByteString.Lazy) as qualified imports to avoid namespace clashes. The configDef function&#8217;s type signature had to change a bit because Parsec 3.1.1 does not have a convenience type alias for LanguageDef that uses bytestrings. To be honest, I couldn&#8217;t figure out the type signatures for p_identifier, p_symbol, etc., so I had GHC do it for me. But I mean, it all took like 5-10 minutes. Easy.</p>
<p>So now you can parse bytestrings &#8212; lazily!</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/zuttobenkyou.wordpress.com/891/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/zuttobenkyou.wordpress.com/891/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/zuttobenkyou.wordpress.com/891/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/zuttobenkyou.wordpress.com/891/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/zuttobenkyou.wordpress.com/891/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/zuttobenkyou.wordpress.com/891/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/zuttobenkyou.wordpress.com/891/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/zuttobenkyou.wordpress.com/891/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/zuttobenkyou.wordpress.com/891/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/zuttobenkyou.wordpress.com/891/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/zuttobenkyou.wordpress.com/891/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/zuttobenkyou.wordpress.com/891/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/zuttobenkyou.wordpress.com/891/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/zuttobenkyou.wordpress.com/891/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=zuttobenkyou.wordpress.com&amp;blog=1384042&amp;post=891&amp;subd=zuttobenkyou&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://zuttobenkyou.wordpress.com/2011/11/09/parsec-example-revisited-again-parsing-lazy-bytestrings/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">Shinobu</media:title>
		</media:content>
	</item>
		<item>
		<title>Parsec Example Revisited: Custom Configuration File Format Meets the Token Module</title>
		<link>http://zuttobenkyou.wordpress.com/2011/11/07/parsec-example-revisited-custom-configuration-file-format-meets-the-token-module/</link>
		<comments>http://zuttobenkyou.wordpress.com/2011/11/07/parsec-example-revisited-custom-configuration-file-format-meets-the-token-module/#comments</comments>
		<pubDate>Mon, 07 Nov 2011 02:42:26 +0000</pubDate>
		<dc:creator>Shinobu</dc:creator>
				<category><![CDATA[Haskell]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[haskell]]></category>
		<category><![CDATA[languagedef]]></category>
		<category><![CDATA[parsec]]></category>
		<category><![CDATA[token]]></category>

		<guid isPermaLink="false">http://zuttobenkyou.wordpress.com/?p=883</guid>
		<description><![CDATA[As promised, here is a solution to one of the exercises I posed here &#8212; namely, to read and parse a configuration file to determine mount options to pass along to the mount command. (No one&#8217;s reading this stuff right now, but whatever. Haskell is still that much fun for me!) That is, the previous [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=zuttobenkyou.wordpress.com&amp;blog=1384042&amp;post=883&amp;subd=zuttobenkyou&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>As promised, here is a solution to one of the exercises I posed <a href="http://zuttobenkyou.wordpress.com/2011/11/01/parsec-and-cmdargs-in-action-a-small-example/" title="Parsec and CmdArgs in Action: A Small Example">here</a> &#8212; namely, to read and parse a configuration file to determine mount options to pass along to the <strong>mount</strong> command. (No one&#8217;s reading this stuff right now, but whatever. Haskell is still that much fun for me!) That is, the previous solution used a rather crude, hard-coded function, <strong>fileSystemArgs</strong>, as follows:</p>
<p><pre class="brush: plain;">
fileSystemArgs :: String -&gt; [(String, String)]
fileSystemArgs user =
    [ (&quot;ext2&quot;, &quot;ext2 -o rw,relatime&quot;)
    , (&quot;vfat&quot;, &quot;vfat -o rw,uid=&quot; ++ user ++ &quot;,gid=&quot; ++ user)
    ]
</pre></p>
<p>I&#8217;ve removed this code in favor of reading such options from a configuration file at runtime, as per the exercise. The code has increased to 452 lines (only about 100 more lines than before, with my verbose style), and we make use of the Text.Parsec.Token module to define a super-simple mini-language that we use in the configuration file (two curly brace blocks that define mount options). We only use bits and pieces of it, but it&#8217;s still handy because we get some super-convenient parser combinators for free &#8212; namely, <strong>braces</strong>, <strong>stringLiteral</strong>, and <strong>whiteSpace</strong> (which are actually aliased to <strong>p_braces</strong>, <strong>p_stringLiteral</strong>, and <strong>p_whiteSpace</strong> in the code). What&#8217;s more, these functions intelligently evade trailing whitespace and comment lines automatically. This is why I love Parsec!!!</p>
<p>So without further ado, here&#8217;s the code (PUBLIC DOMAIN):<br />
<pre class="brush: plain; collapse: true; light: false; toolbar: true;">
-- LICENSE: PUBLIC DOMAIN
{-# LANGUAGE DeriveDataTypeable, RecordWildCards #-}
module Main where

import System.Console.CmdArgs.Implicit
import System.IO
import System.Environment
import System.Exit
import System.Process
import Text.Parsec.Char hiding (upper)
import Text.Parsec.Combinator
import Text.Parsec.Prim
import Text.Parsec.String
import qualified Text.Parsec.Token as PT
import Text.Parsec.Language (emptyDef)
import Control.Monad.Identity

data Opts = Opts
    { all_devices :: Bool
    , unmount :: Bool
    , unmount_all :: Bool
    , discover :: Bool
    , no_color :: Bool
    } deriving (Data, Typeable, Show, Eq)

progOpts :: Opts
progOpts = Opts
    { all_devices = def &amp;= help &quot;mount all USB devices&quot;
    , unmount = def &amp;= help &quot;choose a USB device to unmount&quot;
    , unmount_all = def &amp;= name &quot;U&quot; &amp;= help &quot;unmount all USB devices&quot;
    , discover = def &amp;= help &quot;list all mounted/unmounted USB devices&quot;
    , no_color = def &amp;= help &quot;disable colors&quot;
    }
    &amp;= details
        [ &quot;Notes:&quot;
        , &quot;&quot;
        , &quot;The default behavior without any options is to try to mount a USB device.&quot;
            ++ &quot; Here, `device' means a device under the /dev directory, and in our context, is actually a file system partition.&quot;
            ++ &quot; Many USB drives have only a single partition, in which case the term `device' means both the USB drive and the single partition it has.&quot;
        , &quot;&quot;
        , &quot;Also, allowing the $USER to execute the mount and umount commands with sudo privileges (sudo visudo) will make things less clunky.&quot;
        ]

getOpts :: IO Opts
getOpts = cmdArgs $ progOpts
    &amp;= summary (_PROGRAM_INFO ++ &quot;, &quot; ++ _COPYRIGHT)
    &amp;= program _PROGRAM_NAME
    &amp;= help _PROGRAM_DESC
    &amp;= helpArg [explicit, name &quot;help&quot;, name &quot;h&quot;]
    &amp;= versionArg [explicit, name &quot;version&quot;, name &quot;v&quot;, summary _PROGRAM_INFO]

_PROGRAM_NAME, _PROGRAM_VERSION, _PROGRAM_INFO, _PROGRAM_DESC, _COPYRIGHT :: String
_PROGRAM_NAME = &quot;usbmnt&quot;
_PROGRAM_VERSION = &quot;0.1.0&quot;
_PROGRAM_INFO = _PROGRAM_NAME ++ &quot; version &quot; ++ _PROGRAM_VERSION
_PROGRAM_DESC = &quot;mount/unmount USB device(s)&quot;
_COPYRIGHT = &quot;PUBLIC DOMAIN&quot;

data BlockDevice = BlockDevice
    { shortname :: String
    , uuid :: UUID
    , fsys :: String
    , mountPoint :: MountPoint
    } deriving (Eq)

data MountPoint
    = MPath { path :: FilePath }
    | Swap
    | Unmounted
    | UnknownBlkidVal
    deriving (Eq)

instance Show BlockDevice where
    show BlockDevice{..} = unwords
        [ shortname
        , fsys
        , uuid
        , show mountPoint
        ]

instance Show MountPoint where
    show (MPath path) = path
    show Swap = &quot;Swap&quot;
    show Unmounted = &quot;Unmounted&quot;
    show UnknownBlkidVal = &quot;UnknownBlkidVal&quot;

blockdeviceDefault :: BlockDevice
blockdeviceDefault = BlockDevice
    { shortname = &quot;&quot;
    , uuid = &quot;&quot;
    , fsys = &quot;&quot;
    , mountPoint = MPath {path = &quot;&quot;}
    }

data Config = Config
    { fsyss :: [(String, String)]
    , uuids :: [(UUID, String)]
    } deriving (Eq, Show)

_ALPHANUM :: String
_ALPHANUM = ['a'..'z'] ++ ['A'..'Z'] ++ ['0'..'9']

data Color
    = Red
    | Green
    | Yellow
    | Blue
    | CNone
    deriving (Show, Eq)

colorize :: Color -&gt; String -&gt; String
colorize c s = case c of
    Blue -&gt; &quot;\x1b[1;34m&quot; ++ s ++ &quot;\x1b[0m&quot;
    Green -&gt; &quot;\x1b[1;32m&quot; ++ s ++ &quot;\x1b[0m&quot;
    Red -&gt; &quot;\x1b[1;31m&quot; ++ s ++ &quot;\x1b[0m&quot;
    Yellow -&gt; &quot;\x1b[1;33m&quot; ++ s ++ &quot;\x1b[0m&quot;
    _ -&gt; s

main :: IO ()
main = do
    hSetBuffering stdout NoBuffering
    hSetBuffering stderr NoBuffering
    opts &lt;- getOpts
    homeDir &lt;- getEnv &quot;HOME&quot;
    errNo &lt;- argsCheck opts homeDir
    when (errNo &gt; 0) $ exitWith $ ExitFailure errNo
    (devs, takenPaths) &lt;- getDevices opts
    let configLoc = homeDir ++ &quot;/.usbmnt&quot;
    configSrc &lt;- readFile configLoc
    (confErrNo, config) &lt;- parseConfig configSrc configLoc
    when (confErrNo &gt; 0) $ exitWith $ ExitFailure confErrNo
    let mountablePaths = filter (\p -&gt; not $ elem p takenPaths) $ map (\p -&gt; &quot;/mnt/u&quot; ++ show p) [(0::Int)..]
        devsKV = zip (map show [(1::Int)..]) . zip devs $ mountablePaths
    prog opts config devsKV

argsCheck :: Opts -&gt; String -&gt; IO Int
argsCheck Opts{..} homeDir
    | null homeDir = e &quot;could not get environment variable $HOME&quot; 1
    | otherwise = return 0
    where
        e :: String -&gt; Int -&gt; IO Int
        e str num = errMsg str &gt;&gt; return num

prog :: Opts -&gt; Config -&gt; [(String, (BlockDevice, FilePath))] -&gt; IO ()
prog opts@Opts{..} config devsKV
    | discover = do
        putStrLn &quot;all devices:&quot;
        mapM_ (\(_, (d, _)) -&gt; putStrLn $ cshow d) devsKV
    | otherwise = do
        putStrLn (if (unmount || unmount_all)
            then &quot;USB device(s) to unmount:&quot;
            else &quot;USB device(s) to mount:&quot;)
        mapM_ (\(n, (d, _)) -&gt; putStrLn $ &quot;    &quot; ++ n ++ &quot;) &quot; ++ show' d) devsKV
        putStrLn &quot;&quot;
        mountMenu opts config devsKV
    where
        cshow :: BlockDevice -&gt; String
        cshow b@BlockDevice{..}
            | no_color = show b
            | otherwise = case mountPoint of
                Unmounted -&gt; colorize Green $ show b
                MPath _ -&gt; if not $ null $ getUSBMountPath b
                    then colorize Blue $ show b
                    else show b
                _ -&gt; show b
        show' :: BlockDevice -&gt; String
        show' = if not (unmount || unmount_all)
            then show
            else unwords . init . words . show

mountMenu :: Opts -&gt; Config -&gt; [(String, (BlockDevice, FilePath))] -&gt; IO ()
mountMenu Opts{..} config devsKV
    | unmount = if length devsKV == 1
        then do
            putStrLn &quot;only 1 USB device to unmount&quot;
            tryMount False config (snd . head $ devsKV) &gt;&gt;= exitWith
        else chooseDev prompt devsKV (tryMount False config)
    | unmount_all = do
        putStrLn &quot;unmounting all USB devices...&quot;
        mapM_ (tryMount False config) (map snd devsKV)
        return ()
    | all_devices = do
        putStrLn &quot;mounting all USB devices...&quot;
        mapM_ (tryMount True config) (map snd devsKV)
        return ()
    | length devsKV == 1 = do
        putStrLn &quot;only 1 USB device to mount&quot;
        tryMount True config (snd . head $ devsKV) &gt;&gt;= exitWith
    | otherwise = chooseDev prompt devsKV (tryMount True config)
    where
        prompt :: String
        prompt = if (unmount || unmount_all)
            then &quot;choose USB device to unmount (q to exit)&quot;
            else &quot;choose USB device to mount (q to exit)&quot;

chooseDev :: String -&gt; [(String, (BlockDevice, FilePath))] -&gt; ((BlockDevice, FilePath) -&gt; IO ExitCode) -&gt; IO ()
chooseDev prompt devsKV func = do
    putStrLn prompt
    key &lt;- getLine
    case lookup key devsKV of
        Just dev -&gt; func dev &gt;&gt;= exitWith
        _ -&gt; case key of
            &quot;q&quot; -&gt; return ()
            _ -&gt; chooseDev prompt devsKV func

tryMount :: Bool -&gt; Config -&gt; (BlockDevice, FilePath) -&gt; IO ExitCode
tryMount mount config@Config{..} (bd@BlockDevice{..}, mp)
    | (null margs) = do
        errMsg $ &quot;UUID &quot; ++ squote uuid ++ &quot; was not found in config file&quot;
        errMsg $ &quot;filesystem &quot; ++ squote fsys ++ &quot; was also not found in config file&quot;
        errMsg $ &quot;supported file systems: &quot; ++ (unwords $ map fst fsyss)
        exitWith (ExitFailure 1)
    | otherwise = do
    when mount $ do
        if (null $ mountArgsUUID config uuid)
            then putStrLn $ &quot;filesystem &quot; ++ squote fsys ++ &quot; found in config file&quot;
            else putStrLn $ &quot;UUID &quot; ++ squote uuid ++ &quot; found in config file&quot;
        putStrLn $ &quot;using these arguments: &quot; ++ squote margs
    putStr $ (if mount then &quot;&quot; else &quot;un&quot;)
        ++ &quot;mounting &quot;
        ++ shortname
        ++ &quot; (&quot; ++ fsys ++ &quot;) &quot;
        ++ (if mount then &quot;to &quot; ++ mp else &quot;from &quot; ++ show mountPoint)
        ++ &quot;..&quot;
    (_, _, _, p) &lt;- createProcess $ cmd margs shortname
    exitStatus &lt;- waitForProcess p
    if (exitStatus == ExitSuccess)
        then do
            putStrLn &quot;OK&quot;
            return ExitSuccess
        else do
            putStr &quot;FAILED\n&quot;
            errMsg $ (if mount
                then &quot;mount error (perhaps &quot; ++ squote mp ++ &quot; does not exist)&quot;
                else &quot;unmount error&quot;)
            return (ExitFailure 1)
    where
        margs = mountArgs config bd
        cmd arguments devPath = CreateProcess
            { cmdspec = ShellCommand (if mount
                then &quot;sudo mount -t &quot; ++ arguments ++ &quot; &quot; ++ devPath ++ &quot; &quot; ++ mp ++ &quot; &amp;&gt;/dev/null&quot;
                else &quot;sudo umount &quot; ++ show mountPoint)
            , cwd = Nothing
            , env = Nothing
            , std_in = CreatePipe
            , std_out = CreatePipe
            , std_err = Inherit
            , close_fds = False
            }

mountArgs :: Config -&gt; BlockDevice -&gt; String
mountArgs Config{..} BlockDevice{..} = case lookup uuid uuids of
    Just a -&gt; a
    _ -&gt; case lookup fsys fsyss of
        Just a -&gt; a
        _ -&gt; []

mountArgsUUID :: Config -&gt; UUID -&gt; String
mountArgsUUID Config{..} uuid' = case lookup uuid' uuids of
    Just a -&gt; a
    _ -&gt; []

getDevices :: Opts -&gt; IO ([BlockDevice], [String])
getDevices Opts{..} = do
    (_, sout, _, p) &lt;- createProcess cmdBlkid
    devs &lt;- case sout of
        Just h -&gt; hGetContents h
        Nothing -&gt; return []
    _ &lt;- waitForProcess p
    let devs' = (map (unwords . words)) . drop 2 . lines $ devs
    devs'' &lt;- mapM parseBlkid devs'
    let toMount = filter (\BlockDevice{..} -&gt; mountPoint == Unmounted) devs''
        toUnmount = filter (\dev -&gt; not $ null $ getUSBMountPath dev) devs''
        takenPaths = filter (not . null) . map getUSBMountPath $ devs''
    when (not discover &amp;&amp; null toMount &amp;&amp; (not (unmount || unmount_all))) $ do
        errMsg $ &quot;cannot find USB devices to mount&quot;
        exitWith (ExitFailure 1)
    when (not discover &amp;&amp; null toUnmount &amp;&amp; (unmount || unmount_all)) $ do
        errMsg $ &quot;cannot find USB devices to unmount&quot;
        exitWith (ExitFailure 1)
    return $ formatDevs devs'' toMount toUnmount takenPaths
    where
        formatDevs :: [BlockDevice] -&gt; [BlockDevice] -&gt; [BlockDevice] -&gt; [String] -&gt; ([BlockDevice], [String])
        formatDevs ds m um takenPaths
            | discover = (ds, takenPaths)
            | unmount || unmount_all = (um, takenPaths)
            | otherwise = (m, takenPaths)
        cmdBlkid = CreateProcess
            { cmdspec = ShellCommand (&quot;sudo blkid -o list&quot;)
            , cwd = Nothing
            , env = Nothing
            , std_in = CreatePipe
            , std_out = CreatePipe
            , std_err = Inherit
            , close_fds = False
            }

getUSBMountPath :: BlockDevice -&gt; String
getUSBMountPath BlockDevice{..} = case mountPoint of
    MPath str -&gt; if take 6 str == &quot;/mnt/u&quot; &amp;&amp; (all (\c -&gt; elem c ['0'..'9']) (drop 6 str))
        then str
        else &quot;&quot;
    _ -&gt; &quot;&quot;

errMsg :: String -&gt; IO ()
errMsg msg = hPutStrLn stderr $ &quot;error: &quot; ++ msg

squote :: String -&gt; String
squote s = &quot;`&quot; ++ s ++ &quot;'&quot;

-- Parsing

-- for parsing the computer-generated output of `sudo blkid -o list'
parserIdentifier :: Parser String
parserIdentifier = many1 $ oneOf $ _ALPHANUM ++ &quot;/-_&quot;

parserWhitespace :: Parser String
parserWhitespace = many1 $ oneOf &quot; \t\n\r&quot;

parserMP :: Parser MountPoint
parserMP =
    try ( do
        a &lt;- oneOf &quot;&lt;(&quot;
        b &lt;- manyTill anyChar (lookAhead $ (oneOf &quot;&gt;)&quot;))
        _ &lt;- oneOf &quot;&gt;)&quot;
        let mp = case a of
                '&lt;' -&gt; Swap
                '(' -&gt; case b of
                    &quot;not mounted&quot; -&gt; Unmounted
                    _ -&gt; UnknownBlkidVal
                _ -&gt; UnknownBlkidVal
        return mp
        )
    &lt;|&gt; (parserIdentifier &gt;&gt;= (\s -&gt; return MPath {path = s}))
    &lt;?&gt; &quot;blkid's mount point description&quot;

blkidParser :: Parser BlockDevice
blkidParser =
    try ( do
        sname &lt;- parserIdentifier
        _ &lt;- parserWhitespace
        fs &lt;- parserIdentifier
        _ &lt;- parserWhitespace
        _ &lt;- parserIdentifier -- leave out the &quot;label&quot; column, even if it exists
        _ &lt;- parserWhitespace
        mp &lt;- parserMP
        _ &lt;- parserWhitespace
        uid &lt;- parserIdentifier
        eof
        return BlockDevice
           { shortname = sname
           , uuid = uid
           , fsys = fs
           , mountPoint = mp
           }
        )
    &lt;|&gt;
    do  sname &lt;- parserIdentifier
        _ &lt;- parserWhitespace
        fs &lt;- parserIdentifier
        _ &lt;- parserWhitespace
        mp &lt;- parserMP
        _ &lt;- parserWhitespace
        uid &lt;- parserIdentifier
        eof
        return BlockDevice
            { shortname = sname
            , uuid = uid
            , fsys = fs
            , mountPoint = mp
            }
    &lt;?&gt; &quot;5 or 4 fields to parse&quot;

parseBlkid :: String -&gt; IO BlockDevice
parseBlkid src =
    case parse blkidParser &quot;output of `sudo blkid -o list'&quot; src of
        Left parseError -&gt; errMsg (show parseError) &gt;&gt; return blockdeviceDefault
        Right result -&gt; return result

-- we use a LanguageDef so that we can get whitespace/newline parsing for FREE
-- in our .usbmnt file
configDef :: PT.LanguageDef st
configDef = emptyDef
    { PT.commentStart   = &quot;&quot;
    , PT.commentEnd     = &quot;&quot;
    , PT.commentLine    = &quot;#&quot;
    , PT.nestedComments = False
    -- the identStart/identLetter define what a UUID will look like (a
    -- dash-separated hex number)
    , PT.identStart     = oneOf $ ['0'..'9'] ++ ['a'..'f'] ++ ['A'..'F']
    , PT.identLetter    = oneOf $ ['0'..'9'] ++ ['a'..'f'] ++ ['A'..'F'] ++ &quot;-&quot;
    , PT.opStart        = char '.'
    , PT.opLetter       = char '.'
    , PT.reservedOpNames= []
    , PT.reservedNames  = []
    , PT.caseSensitive  = True
    }

-- we call makeTokenParser def and pick out just those we need
lexer :: PT.TokenParser ()
lexer = PT.makeTokenParser configDef

p_identifier :: ParsecT String () Identity String
p_identifier = PT.identifier lexer
p_stringLiteral :: ParsecT String () Identity String
p_stringLiteral = PT.stringLiteral lexer
p_whiteSpace :: ParsecT String () Identity ()
p_whiteSpace = PT.whiteSpace lexer
p_braces :: ParsecT String () Identity a -&gt; ParsecT String () Identity a
p_braces = PT.braces lexer
p_commaSep :: ParsecT String () Identity a -&gt; ParsecT String () Identity [a]
p_commaSep = PT.commaSep lexer
p_symbol :: String -&gt; ParsecT String () Identity String
p_symbol = PT.symbol lexer

type UUID = String

assocParser :: Parser String -&gt; Parser (UUID, String)
assocParser keyParser = do
    key &lt;- keyParser
    _ &lt;- many $ oneOf &quot; \t&quot;
    _ &lt;- string &quot;=&quot;
    _ &lt;- many $ oneOf &quot; \t&quot;
    mountOpts &lt;- p_stringLiteral
    return (key, mountOpts)
    &lt;?&gt; &quot;a key-value association&quot;

hashParser :: String -&gt; Parser String -&gt; Parser [(String, String)]
hashParser hashName keyParser = do
    _ &lt;- p_symbol hashName
    _ &lt;- p_symbol &quot;=&quot;
    a &lt;- p_braces (p_commaSep $ assocParser keyParser)
    return a
    &lt;?&gt; &quot;a &quot; ++ hashName ++ &quot; curly brace block&quot;

configParser :: Parser Config
configParser = do
    p_whiteSpace -- take care of leading whitespace/comments as defined by configDef
    -- parse FSYS_HASH first
    fsyss' &lt;- hashParser &quot;FSYS_HASH&quot; (many1 alphaNum)
    p_whiteSpace
    -- now parse UUID_HASH
    uuids' &lt;- hashParser &quot;UUID_HASH&quot; (p_identifier)
    eof
    return $ Config {fsyss = fsyss', uuids = uuids'}
    &lt;?&gt; &quot;config with FSYS_HASH and UUID_HASH blocks&quot;

parseConfig :: String -&gt; String -&gt; IO (Int, Config)
parseConfig src loc =
    case parse configParser (&quot;config file at &quot; ++ squote loc) src of
        Left parseError -&gt; errMsg (show parseError) &gt;&gt; return (1, Config [] [])
        Right result -&gt; return (0, result)
</pre></p>
<p>And here is the new <strong>~/.usbmnt</strong> configuration file that it parses:<br />
<pre class="brush: plain;">
# UUID/filesystem mountoptions

# the FSYS_HASH is used as a fallback, if the detected device does not match one of the UUIDs listed above
FSYS_HASH =
    { ext2 = &quot;ext2 -o rw,relatime&quot;
    , vfat = &quot;vfat -o rw,uid=$USER,gid=$USER&quot;
    }

# UUID is pretty straightforward; use blkid to figure it out for a particular device
# as for mount options, these are passed directly into the shell (/bin/sh; see createProcess in System.Process), so you can use things like $USER

# UUID_HASH = {} # if you have no particular device-specific settings

UUID_HASH =
    { 7CBF-B36F =                             &quot;vfat -o rw,uid=$USER,gid=$USER&quot;    # (256 MiB) SD card
    , 9c359666-a4e6-a894-3475-e6cd53660de8 =  &quot;ext2 -o rw,relatime&quot;               # (2 GiB) USB 2.0 thumbdrive
    }
</pre></p>
<p>See how there are a bunch of comment lines starting with &#8216;<strong>#</strong>&#8216;? With our configDef function which defines a language with &#8220;#&#8221; as the <strong>commentLine</strong>, our convenience functions p_whiteSpace, p_symbol, etc. all avoid comment lines automatically! Sure, it&#8217;s a trivial automation here, but you can also define multiline comments and more (which I didn&#8217;t bother to do since the configuration file format I designed is so simple). I really like how I can robustly parse comment-laden text strings with such ease!</p>
<p>Can you imagine doing the same thing with regular expressions? I used to use regexes for parsing things like this back when I was still a very naive programmer, and then Parsec opened up the wonderful world of parsers for me (especially all the cool convenience functions you get for FREE!). The new rule I have is: use regular expressions to strictly do search-and-replace for unchanging/uniform input (using the Text.Regex.PCRE module), and to use Parsec for everything else. There&#8217;s no excuse to put off learning how to use Parsec!</p>
<p>Anyway, the code I posted is a working example and I really do use it in my machines! And all you need is a POSIX system with GHC, Parsec, and CmdArgs to use it.</p>
<p>Next time, I&#8217;ll do the second exercise and read in and parse ByteStrings instead of native Haskell Strings &#8212; just to show you how trivial the changes are!</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/zuttobenkyou.wordpress.com/883/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/zuttobenkyou.wordpress.com/883/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/zuttobenkyou.wordpress.com/883/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/zuttobenkyou.wordpress.com/883/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/zuttobenkyou.wordpress.com/883/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/zuttobenkyou.wordpress.com/883/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/zuttobenkyou.wordpress.com/883/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/zuttobenkyou.wordpress.com/883/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/zuttobenkyou.wordpress.com/883/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/zuttobenkyou.wordpress.com/883/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/zuttobenkyou.wordpress.com/883/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/zuttobenkyou.wordpress.com/883/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/zuttobenkyou.wordpress.com/883/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/zuttobenkyou.wordpress.com/883/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=zuttobenkyou.wordpress.com&amp;blog=1384042&amp;post=883&amp;subd=zuttobenkyou&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://zuttobenkyou.wordpress.com/2011/11/07/parsec-example-revisited-custom-configuration-file-format-meets-the-token-module/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">Shinobu</media:title>
		</media:content>
	</item>
		<item>
		<title>Parsec and CmdArgs in Action: A Small Example</title>
		<link>http://zuttobenkyou.wordpress.com/2011/11/01/parsec-and-cmdargs-in-action-a-small-example/</link>
		<comments>http://zuttobenkyou.wordpress.com/2011/11/01/parsec-and-cmdargs-in-action-a-small-example/#comments</comments>
		<pubDate>Tue, 01 Nov 2011 01:32:52 +0000</pubDate>
		<dc:creator>Shinobu</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Haskell]]></category>
		<category><![CDATA[haskell]]></category>
		<category><![CDATA[cmdargs]]></category>
		<category><![CDATA[example]]></category>
		<category><![CDATA[script]]></category>
		<category><![CDATA[blkid]]></category>
		<category><![CDATA[USB]]></category>
		<category><![CDATA[parsec]]></category>

		<guid isPermaLink="false">http://zuttobenkyou.wordpress.com/?p=877</guid>
		<description><![CDATA[In this post, I mentioned that I wrote a ~300 line Haskell program to take care of mounting/unmounting USB drives (especially useful for window-manager-only users like myself). Well, I&#8217;ve been using my program (creatively named usbmnt) very happily so far and would like to release it for public consumption. It&#8217;s released into the PUBLIC DOMAIN, [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=zuttobenkyou.wordpress.com&amp;blog=1384042&amp;post=877&amp;subd=zuttobenkyou&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>In <a href="http://zuttobenkyou.wordpress.com/2011/07/28/detecting-unmounted-partitions-with-blkid/" title="Detecting Unmounted Partitions With Blkid">this</a> post, I mentioned that I wrote a ~300 line Haskell program to take care of mounting/unmounting USB drives (especially useful for window-manager-only users like myself). Well, I&#8217;ve been using my program (creatively named <strong>usbmnt</strong>) very happily so far and would like to release it for public consumption. It&#8217;s released into the PUBLIC DOMAIN, because that&#8217;s how I roll, baby!</p>
<p><pre class="brush: plain; collapse: true; light: false; toolbar: true;">
-- LICENSE: PUBLIC DOMAIN
{-# LANGUAGE DeriveDataTypeable, RecordWildCards #-}
module Main where

import Control.Monad (when)
import System.Console.CmdArgs.Implicit
import System.IO
import System.Environment
import System.Exit
import System.Process
import Text.Parsec.Char hiding (upper)
import Text.Parsec.Combinator
import Text.Parsec.Prim
import Text.Parsec.String

data Opts = Opts
    { all_devices :: Bool
    , unmount :: Bool
    , unmount_all :: Bool
    , discover :: Bool
    , no_color :: Bool
    } deriving (Data, Typeable, Show, Eq)

progOpts :: Opts
progOpts = Opts
    { all_devices = def &amp;= help &quot;mount all USB devices&quot;
    , unmount = def &amp;= help &quot;choose a USB device to unmount&quot;
    , unmount_all = def &amp;= name &quot;U&quot; &amp;= help &quot;unmount all USB devices&quot;
    , discover = def &amp;= help &quot;list all mounted/unmounted USB devices&quot;
    , no_color = def &amp;= help &quot;disable colors&quot;
    }
    &amp;= details
        [ &quot;Notes:&quot;
        , &quot;&quot;
        , &quot;The default behavior without any options is to try to mount a USB device.&quot;
            ++ &quot; Here, `device' means a device under the /dev directory, and in our context, is actually a file system partition.&quot;
            ++ &quot; Many USB drives have only a single partition, in which case the term `device' means both the USB drive and the single partition it has.&quot;
        , &quot;&quot;
        , &quot;Also, allowing the $USER to execute the mount and umount commands with sudo privileges (sudo visudo) will make things less clunky.&quot;
        ]

getOpts :: IO Opts
getOpts = cmdArgs $ progOpts
    &amp;= summary (_PROGRAM_INFO ++ &quot;, &quot; ++ _COPYRIGHT)
    &amp;= program _PROGRAM_NAME
    &amp;= help _PROGRAM_DESC
    &amp;= helpArg [explicit, name &quot;help&quot;, name &quot;h&quot;]
    &amp;= versionArg [explicit, name &quot;version&quot;, name &quot;v&quot;, summary _PROGRAM_INFO]

_PROGRAM_NAME, _PROGRAM_VERSION, _PROGRAM_INFO, _PROGRAM_DESC, _COPYRIGHT :: String
_PROGRAM_NAME = &quot;usbmnt&quot;
_PROGRAM_VERSION = &quot;0.0.1&quot;
_PROGRAM_INFO = _PROGRAM_NAME ++ &quot; version &quot; ++ _PROGRAM_VERSION
_PROGRAM_DESC = &quot;mount/unmount USB device(s)&quot;
_COPYRIGHT = &quot;PUBLIC DOMAIN&quot;

data BlockDevice = BlockDevice
    { shortname :: String
    , uuid :: String
    , fsys :: String
    , mountPoint :: MountPoint
    } deriving (Eq)

data MountPoint
    = MPath { path :: FilePath }
    | Swap
    | Unmounted
    | UnknownBlkidVal
    deriving (Eq)

instance Show BlockDevice where
    show BlockDevice{..} = unwords
        [ shortname
        , fsys
        , uuid
        , show mountPoint
        ]

instance Show MountPoint where
    show (MPath path) = path
    show Swap = &quot;Swap&quot;
    show Unmounted = &quot;Unmounted&quot;
    show UnknownBlkidVal = &quot;UnknownBlkidVal&quot;

blockdeviceDefault :: BlockDevice
blockdeviceDefault = BlockDevice
    { shortname = &quot;&quot;
    , uuid = &quot;&quot;
    , fsys = &quot;&quot;
    , mountPoint = MPath {path = &quot;&quot;}
    }

_ALPHANUM :: String
_ALPHANUM = ['a'..'z'] ++ ['A'..'Z'] ++ ['0'..'9']

data Color
    = Red
    | Green
    | Yellow
    | Blue
    | CNone
    deriving (Show, Eq)

colorize :: Color -&gt; String -&gt; String
colorize c s = case c of
    Blue -&gt; &quot;\x1b[1;34m&quot; ++ s ++ &quot;\x1b[0m&quot;
    Green -&gt; &quot;\x1b[1;32m&quot; ++ s ++ &quot;\x1b[0m&quot;
    Red -&gt; &quot;\x1b[1;31m&quot; ++ s ++ &quot;\x1b[0m&quot;
    Yellow -&gt; &quot;\x1b[1;33m&quot; ++ s ++ &quot;\x1b[0m&quot;
    _ -&gt; s

main :: IO ()
main = do
    hSetBuffering stdout NoBuffering
    hSetBuffering stderr NoBuffering
    opts &lt;- getOpts
    user &lt;- getEnv &quot;USER&quot;
    errNo &lt;- argsCheck opts user
    when (errNo &gt; 0) $ exitWith $ ExitFailure errNo
    (devs, takenPaths) &lt;- getDevices opts
    let mountablePaths = filter (\p -&gt; not $ elem p takenPaths) $ map (\p -&gt; &quot;/mnt/u&quot; ++ show p) [(0::Int)..]
        devsKV = zip (map show [(1::Int)..]) . zip devs $ mountablePaths
    prog opts user devsKV

argsCheck :: Opts -&gt; String -&gt; IO Int
argsCheck Opts{..} user
    | null user = e &quot;could not get environment variable $USER&quot; 1
    | otherwise = return 0
    where
        e :: String -&gt; Int -&gt; IO Int
        e str num = errMsg str &gt;&gt; return num

prog :: Opts -&gt; String -&gt; [(String, (BlockDevice, FilePath))] -&gt; IO ()
prog opts@Opts{..} user devsKV
    | discover = do
        putStrLn &quot;all devices:&quot;
        mapM_ (\(_, (d, _)) -&gt; putStrLn $ cshow d) devsKV
    | otherwise = do
        putStrLn (if (unmount || unmount_all)
            then &quot;USB device(s) to unmount:&quot;
            else &quot;USB device(s) to mount:&quot;)
        mapM_ (\(n, (d, _)) -&gt; putStrLn $ &quot;    &quot; ++ n ++ &quot;) &quot; ++ show' d) devsKV
        putStrLn &quot;&quot;
        mountMenu opts user devsKV
    where
        cshow :: BlockDevice -&gt; String
        cshow b@BlockDevice{..}
            | no_color = show b
            | otherwise = case mountPoint of
                Unmounted -&gt; colorize Green $ show b
                MPath _ -&gt; if not $ null $ getUSBMountPath b
                    then colorize Blue $ show b
                    else show b
                _ -&gt; show b
        show' :: BlockDevice -&gt; String
        show' = if not (unmount || unmount_all)
            then show
            else unwords . init . words . show

mountMenu :: Opts -&gt; String -&gt; [(String, (BlockDevice, FilePath))] -&gt; IO ()
mountMenu Opts{..} user devsKV
    | unmount = if length devsKV == 1
        then do
            putStrLn &quot;only 1 USB device to unmount&quot;
            tryMount False user (snd . head $ devsKV) &gt;&gt;= exitWith
        else chooseDev prompt user devsKV (tryMount False)
    | unmount_all = do
        putStrLn &quot;unmounting all USB devices...&quot;
        mapM_ (tryMount False user) (map snd devsKV)
        return ()
    | all_devices = do
        putStrLn &quot;mounting all USB devices...&quot;
        mapM_ (tryMount True user) (map snd devsKV)
        return ()
    | length devsKV == 1 = do
        putStrLn &quot;only 1 USB device to mount&quot;
        tryMount True user (snd . head $ devsKV) &gt;&gt;= exitWith
    | otherwise = chooseDev prompt  user devsKV (tryMount True)
    where
        prompt :: String
        prompt = if (unmount || unmount_all)
            then &quot;choose USB device to unmount (q to exit)&quot;
            else &quot;choose USB device to mount (q to exit)&quot;

chooseDev :: String -&gt; String -&gt; [(String, (BlockDevice, FilePath))] -&gt; (String -&gt; (BlockDevice, FilePath) -&gt; IO ExitCode) -&gt; IO ()
chooseDev prompt user devsKV func = do
    putStrLn prompt
    key &lt;- getLine
    case lookup key devsKV of
        Just dev -&gt; func user dev &gt;&gt;= exitWith
        _ -&gt; case key of
            &quot;q&quot; -&gt; return ()
            _ -&gt; chooseDev prompt user devsKV func

tryMount :: Bool -&gt; String -&gt; (BlockDevice, FilePath) -&gt; IO ExitCode
tryMount mount user (BlockDevice{..}, mp) = do
    when (null $ mountArgs fsys user) $ do
        errMsg $ &quot;unsupported file system &quot; ++ squote fsys ++ &quot;\nsupported file systems: &quot; ++ (unwords $ map fst (fileSystemArgs user))
        exitWith (ExitFailure 1)
    putStr $ (if mount then &quot;&quot; else &quot;un&quot;)
        ++ &quot;mounting &quot;
        ++ shortname
        ++ &quot; (&quot; ++ fsys ++ &quot;) &quot;
        ++ (if mount then &quot;to &quot; ++ mp else &quot;from &quot; ++ show mountPoint)
        ++ &quot;..&quot;
    (_, _, _, p) &lt;- createProcess $ cmd (mountArgs fsys user) shortname
    exitStatus &lt;- waitForProcess p
    if (exitStatus == ExitSuccess)
        then do
            putStrLn &quot;OK&quot;
            return ExitSuccess
        else do
            putStr &quot;FAILED\n&quot;
            errMsg $ (if mount
                then &quot;mount error (perhaps &quot; ++ squote mp ++ &quot; does not exist)&quot;
                else &quot;unmount error&quot;)
            return (ExitFailure 1)
    where
        cmd arguments devPath = CreateProcess
            { cmdspec = ShellCommand (if mount
                then &quot;sudo mount -t &quot; ++ arguments ++ &quot; &quot; ++ devPath ++ &quot; &quot; ++ mp ++ &quot; &amp;&gt;/dev/null&quot;
                else &quot;sudo umount &quot; ++ show mountPoint)
            , cwd = Nothing
            , env = Nothing
            , std_in = CreatePipe
            , std_out = CreatePipe
            , std_err = Inherit
            , close_fds = False
            }

fileSystemArgs :: String -&gt; [(String, String)]
fileSystemArgs user =
    [ (&quot;ext2&quot;, &quot;ext2 -o rw,relatime&quot;)
    , (&quot;vfat&quot;, &quot;vfat -o rw,uid=&quot; ++ user ++ &quot;,gid=&quot; ++ user)
    ]

mountArgs :: String -&gt; String -&gt; String
mountArgs fsys user = case lookup fsys (fileSystemArgs user) of
    Just a -&gt; a
    _ -&gt; []

getDevices :: Opts -&gt; IO ([BlockDevice], [String])
getDevices Opts{..} = do
    (_, sout, _, p) &lt;- createProcess cmdBlkid
    devs &lt;- case sout of
        Just h -&gt; hGetContents h
        Nothing -&gt; return []
    _ &lt;- waitForProcess p
    let devs' = (map (unwords . words)) . drop 2 . lines $ devs
    devs'' &lt;- mapM parseBlkid devs'
    let toMount = filter (\BlockDevice{..} -&gt; mountPoint == Unmounted) devs''
        toUnmount = filter (\dev -&gt; not $ null $ getUSBMountPath dev) devs''
        takenPaths = filter (not . null) . map getUSBMountPath $ devs''
    when (not discover &amp;&amp; null toMount &amp;&amp; (not (unmount || unmount_all))) $ do
        errMsg $ &quot;cannot find USB devices to mount&quot;
        exitWith (ExitFailure 1)
    when (not discover &amp;&amp; null toUnmount &amp;&amp; (unmount || unmount_all)) $ do
        errMsg $ &quot;cannot find USB devices to unmount&quot;
        exitWith (ExitFailure 1)
    return $ formatDevs devs'' toMount toUnmount takenPaths
    where
        formatDevs :: [BlockDevice] -&gt; [BlockDevice] -&gt; [BlockDevice] -&gt; [String] -&gt; ([BlockDevice], [String])
        formatDevs ds m um takenPaths
            | discover = (ds, takenPaths)
            | unmount || unmount_all = (um, takenPaths)
            | otherwise = (m, takenPaths)
        cmdBlkid = CreateProcess
            { cmdspec = ShellCommand (&quot;sudo blkid -o list&quot;)
            , cwd = Nothing
            , env = Nothing
            , std_in = CreatePipe
            , std_out = CreatePipe
            , std_err = Inherit
            , close_fds = False
            }

getUSBMountPath :: BlockDevice -&gt; String
getUSBMountPath BlockDevice{..} = case mountPoint of
    MPath str -&gt; if take 6 str == &quot;/mnt/u&quot; &amp;&amp; (all (\c -&gt; elem c ['0'..'9']) (drop 6 str))
        then str
        else &quot;&quot;
    _ -&gt; &quot;&quot;

errMsg :: String -&gt; IO ()
errMsg msg = hPutStrLn stderr $ &quot;error: &quot; ++ msg

squote :: String -&gt; String
squote s = &quot;`&quot; ++ s ++ &quot;'&quot;

-- Parsing
parserIdentifier :: Parser String
parserIdentifier = many1 $ oneOf $ _ALPHANUM ++ &quot;/-_&quot;

parserWhitespace :: Parser String
parserWhitespace = many1 $ oneOf &quot; \t\n\r&quot;

parserMP :: Parser MountPoint
parserMP =
    try ( do
        a &lt;- oneOf &quot;&lt;(&quot; -- &quot;(not mounted)&quot; or &quot;&lt;swap&gt;&quot;
        b &lt;- manyTill anyChar (lookAhead $ (oneOf &quot;&gt;)&quot;))
        _ &lt;- oneOf &quot;&gt;)&quot;
        let mp = case a of
                '&lt;' -&gt; Swap
                '(' -&gt; case b of
                    &quot;not mounted&quot; -&gt; Unmounted
                    _ -&gt; UnknownBlkidVal
                _ -&gt; UnknownBlkidVal
        return mp
        )
    &lt;|&gt; (parserIdentifier &gt;&gt;= (\s -&gt; return MPath {path = s})) -- e.g., &quot;/mnt/blah&quot;
    &lt;?&gt; &quot;blkid's mount point description&quot;

blkidParser :: Parser BlockDevice
blkidParser =
    try ( do
        sname &lt;- parserIdentifier
        _ &lt;- parserWhitespace
        fs &lt;- parserIdentifier
        _ &lt;- parserWhitespace
        _ &lt;- parserIdentifier -- leave out the &quot;label&quot; column, even if it exists
        _ &lt;- parserWhitespace
        mp &lt;- parserMP
        _ &lt;- parserWhitespace
        uid &lt;- parserIdentifier
        eof
        return BlockDevice
           { shortname = sname
           , uuid = uid
           , fsys = fs
           , mountPoint = mp
           }
        )
    &lt;|&gt;
    do  sname &lt;- parserIdentifier
        _ &lt;- parserWhitespace
        fs &lt;- parserIdentifier
        _ &lt;- parserWhitespace
        mp &lt;- parserMP
        _ &lt;- parserWhitespace
        uid &lt;- parserIdentifier
        eof
        return BlockDevice
            { shortname = sname
            , uuid = uid
            , fsys = fs
            , mountPoint = mp
            }
    &lt;?&gt; &quot;5 or 4 fields to parse&quot;

parseBlkid :: String -&gt; IO BlockDevice
parseBlkid src =
    case parse blkidParser &quot;output of `sudo blkid -o list'&quot; src of
        Left parseError -&gt; errMsg (show parseError) &gt;&gt; return blockdeviceDefault
        Right result -&gt; return result
</pre></p>
<p>This example compiles with <strong>ghc &#8211;make -O2 -Wall -Werror</strong>. The only 2 dependencies are Parsec, and CmdArgs.</p>
<p>Looking back at it, I think it&#8217;s a great example of a real-world Haskell program out in the wild. It uses Parsec to correctly parse the output from <strong>blkid</strong>, CmdArgs for sane argument handling, and also does some system calls with the <strong>createProcess</strong> command. Coding-style-wise, it uses the simple <strong>where</strong> expression wherever possible, for maximum readability and tries to keep leading whitespace to a minimum. The code is written very verbosely, with explicit type signatures for every function, because, well, it helps me keep my sanity.</p>
<p>I think the code is pretty straightforward. It only supports 2 file systems: the ext2 type (recommended for Linux-only USB drives), and vfat (the FAT file system used for legacy Windows support), but this could be easily extended to support ext3, ext4, or any other file system, since it just wraps around the standard <strong>mount</strong> command.</p>
<p>The only tricky part is <strong>blkidParser</strong>, which tries to parse 5 fields or 4 fields. The reason behind this is because<strong> blkid -o&#8217;</strong>s output looks like this:</p>
<p><pre class="brush: plain;">
$ sudo blkid -o list
device       fs_type label    mount point      UUID
-----------------------------------------------------------------------------------
/dev/sda1    ntfs             /mnt/windows-xp  XXXXXXXXXXXXXXXX
/dev/sda2    ext4             /                XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
/dev/sda3    ext4             /home            XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
/dev/sda6    swap             &lt;swap&gt;           XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
/dev/sda5    ext4             /mnt/data        XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
/dev/sdb1    ext2             (not mounted)    XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
/dev/sdc1    vfat             (not mounted)    XXXX-XXXX
</pre></p>
<p>Sometimes, the <strong>label</strong> field is empty, as in the example above. So, we will end up with just 4 fields instead of 5.</p>
<p>I hope this sample, working program will help newbies out (esp. you who are frustrated by Real World Haskell&#8217;s clunky Parsec examples&#8230;). And for those looking to actually use it themselves for easy USB mounting/unmounting, here are some additional notes:</p>
<ul>
<li>Create mount points (i.e., directories) /mnt/u0, /mnt/u1, /mnt/u2, etc. so that the mount command won&#8217;t choke.</li>
<li>Use shell aliases or keyboard hotkeys to avoid typing out &#8220;usbmnt -U&#8221; every single time.</li>
<li>Tweak the options passed to the mount command to suit your needs (see the <strong>fileSystemArgs</strong> function).</li>
</ul>
<p>(1) A simple exercise would be to extend usbmnt so that it reads a very simple configuration file to detect the right user-defined options to pass to <strong>mount</strong> depending on the partition&#8217;s UUID. The only additional thing you would have to do is read a file from disk and store it as a String.</p>
<p>(2) An eye-opening exercises for Parsec newcomers would be to pass to the parsing functions a ByteString instead of String. (The transition to a ByteString is extremely straight-forward, and requires minimal changes.)</p>
<p>I will do the exercises myself later on and post them in a couple weeks at the latest.</p>
<p>UPDATE: December 1, 2011: Here are some convenience links for those coming in from Google for the solutions to the 2 exercises: <a href="http://zuttobenkyou.wordpress.com/2011/11/07/parsec-example-revisited-custom-configuration-file-format-meets-the-token-module/" title="Parsec Example Revisited: Custom Configuration File Format Meets the Token Module">Solution for #1</a> <a href="http://zuttobenkyou.wordpress.com/2011/11/09/parsec-example-revisited-again-parsing-lazy-bytestrings/" title="Parsec Example Revisited (Again): Parsing Lazy ByteStrings">Solution for #2</a></p>
<p>Happy hacking!</p>
<p>EDIT: December 1, 2011: Fix typo.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/zuttobenkyou.wordpress.com/877/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/zuttobenkyou.wordpress.com/877/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/zuttobenkyou.wordpress.com/877/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/zuttobenkyou.wordpress.com/877/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/zuttobenkyou.wordpress.com/877/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/zuttobenkyou.wordpress.com/877/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/zuttobenkyou.wordpress.com/877/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/zuttobenkyou.wordpress.com/877/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/zuttobenkyou.wordpress.com/877/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/zuttobenkyou.wordpress.com/877/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/zuttobenkyou.wordpress.com/877/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/zuttobenkyou.wordpress.com/877/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/zuttobenkyou.wordpress.com/877/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/zuttobenkyou.wordpress.com/877/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=zuttobenkyou.wordpress.com&amp;blog=1384042&amp;post=877&amp;subd=zuttobenkyou&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://zuttobenkyou.wordpress.com/2011/11/01/parsec-and-cmdargs-in-action-a-small-example/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">Shinobu</media:title>
		</media:content>
	</item>
		<item>
		<title>wxHaskell: Scaling a Bitmap Image</title>
		<link>http://zuttobenkyou.wordpress.com/2011/10/08/wxhaskell-scaling-a-bitmap-image/</link>
		<comments>http://zuttobenkyou.wordpress.com/2011/10/08/wxhaskell-scaling-a-bitmap-image/#comments</comments>
		<pubDate>Sat, 08 Oct 2011 03:44:29 +0000</pubDate>
		<dc:creator>Shinobu</dc:creator>
				<category><![CDATA[Haskell]]></category>
		<category><![CDATA[bitmap]]></category>
		<category><![CDATA[wxHaskell]]></category>

		<guid isPermaLink="false">http://zuttobenkyou.wordpress.com/?p=867</guid>
		<description><![CDATA[I ran into this page while trying to figure out how to scale a bitmap image from within wxHaskell. The solution posted by the OP there scales the drawing context (e.g., the canvas) and not the bitmap itself. To scale the bitmap itself, just do it like this: The key is to load up the [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=zuttobenkyou.wordpress.com&amp;blog=1384042&amp;post=867&amp;subd=zuttobenkyou&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>I ran into <a href="http://stackoverflow.com/questions/7270956/draw-a-scaled-bitmap-using-wxhaskell">this page</a> while trying to figure out how to scale a bitmap image from within wxHaskell. The solution posted by the OP there scales the drawing context (e.g., the canvas) and not the bitmap itself.</p>
<p>To scale the bitmap itself, just do it like this:</p>
<p><pre class="brush: plain;">
import Graphics.UI.WX
import Graphics.UI.WXCore

scaleBitmap ... = do
    imgScaled &lt;- imageConvertToBitmap =&lt;&lt; imageScale img (sz newWidth newHeight)
    -- do stuff with imgScaled; e.g., draw it somewhere with drawBitmap
    bitmapDelete imgScaled
    where
        img = image &quot;foo.bmp&quot;
        newWidth = 100
        newHeight = 20
</pre></p>
<p>The key is to load up the bitmap with the generic <strong>image</strong> function, and not the usual <strong>bitmap</strong> function. This way, you can make use of the <strong>imageScale</strong> function to scale your image, and then you can convert it back again to bitmap format with imageConvertToBitmap. Finally, when we are done with drawing this image somewhere, we free the memory used up to create the bitmap version of it, by calling <strong>bitmapDelete</strong>. A bit clunky, but simple enough. I tested it with a Windows BMP with an alpha channel and it worked quite nicely.</p>
<p>By the way, wxHaskell is very easy to learn &#8212; you just need to skim <a href="http://research.microsoft.com/pubs/66810/wxhaskell.pdf">Daan Leijen&#8217;s paper</a> whenever you get stuck. (Leijen also wrote the beautifully powerful Parsec library &#8212; another library I enjoyed using once I got the hang of it.)</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/zuttobenkyou.wordpress.com/867/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/zuttobenkyou.wordpress.com/867/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/zuttobenkyou.wordpress.com/867/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/zuttobenkyou.wordpress.com/867/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/zuttobenkyou.wordpress.com/867/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/zuttobenkyou.wordpress.com/867/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/zuttobenkyou.wordpress.com/867/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/zuttobenkyou.wordpress.com/867/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/zuttobenkyou.wordpress.com/867/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/zuttobenkyou.wordpress.com/867/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/zuttobenkyou.wordpress.com/867/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/zuttobenkyou.wordpress.com/867/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/zuttobenkyou.wordpress.com/867/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/zuttobenkyou.wordpress.com/867/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=zuttobenkyou.wordpress.com&amp;blog=1384042&amp;post=867&amp;subd=zuttobenkyou&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://zuttobenkyou.wordpress.com/2011/10/08/wxhaskell-scaling-a-bitmap-image/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">Shinobu</media:title>
		</media:content>
	</item>
		<item>
		<title>Website Passwords: A Big Mess</title>
		<link>http://zuttobenkyou.wordpress.com/2011/10/03/website-passwords-a-big-mess/</link>
		<comments>http://zuttobenkyou.wordpress.com/2011/10/03/website-passwords-a-big-mess/#comments</comments>
		<pubDate>Mon, 03 Oct 2011 20:54:03 +0000</pubDate>
		<dc:creator>Shinobu</dc:creator>
				<category><![CDATA[Rant]]></category>
		<category><![CDATA[passwords]]></category>

		<guid isPermaLink="false">http://zuttobenkyou.wordpress.com/?p=860</guid>
		<description><![CDATA[TL;DR: You will inevitably be screwed when you try to change your website passwords. So a few months ago, I changed all of my website passwords. I used a simple pseudorandom ASCII-only character generator to ensure the uniqueness of each one. In the process, I discovered that many websites have horrible, broken password interfaces. This [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=zuttobenkyou.wordpress.com&amp;blog=1384042&amp;post=860&amp;subd=zuttobenkyou&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>TL;DR: You will inevitably be screwed when you try to change your website passwords.</p>
<p>So a few months ago, I changed all of my website passwords. I used a simple pseudorandom ASCII-only character generator to ensure the uniqueness of each one. In the process, I discovered that many websites have horrible, broken password interfaces.</p>
<p>This post is mainly a rant. Setting and changing passwords should never be difficult, and should be 100% transparent. We end users probably collectively wasted millions of hours with broken password interfaces, and will waste millions more until the issues below are addressed each time someone deploys a new website.</p>
<h2>Special Characters</h2>
<p>Many websites tell you a list of special characters that are not allowed in passwords. Sadly, this list is often incomplete. Worse still, some only accept alphanumeric passwords, but are silent as to this restriction &#8212; and to top it off, they don&#8217;t even bother to tell you <strong>why</strong> your chosen password is invalid! The gall.</p>
<p>It appears that the restriction against special characters is largely a matter of legacy vs. modern platforms. Newer websites like Wikipedia allow you to choose any character from a US ASCII keyboard. Many older institutions (Bank of America, for example) have very strange special character restrictions, which almost seem arbitrary (did you know that Bank of America calls passwords &#8220;passcodes&#8221;?).</p>
<p><strong>What needs to be done</strong>: At a minimum, allow input of ALL characters from a US ASCII keyboard ([a-zA-Z0-9] and all punctuation characters <em><strong>and</strong></em> spaces (tabs are impossible to type into a text field in some browsers, so they can be excused)).</p>
<h2>Password Length</h2>
<p>This is the biggest problem. For roughly 1/2 of my website passwords, they have a maximum character limit. Some even enforce a 12-character limit (socalgas.com is one example). Some enforce a 16-character limit (bugs.freedesktop.org, login.live.com). Barnesandnoble.com has a 15-character limit (no space s allowed, alphanumeric only).</p>
<p>But the best part is this: many of these sites <strong><em>do not tell you about this limit</em></strong>. So, you can spend 5, 10 minutes thinking out a great mnemonic device for a <a href="http://xkcd.com/936/">fantastic password</a>, and you&#8217;ll get hit with some &#8220;Invalid Password&#8221; error. Yet another well-meaning user slapped in the face.</p>
<p>Many sites are fixated on only preventing 3, 4 character passwords by implementing an interactive &#8220;password strength&#8221; meter that rejects short passwords. But they still fail to tell you that your password is too long.</p>
<p>EDIT: Bela pointed out in the comments another common bug: the site will happily accept your chosen password, but will truncate it to a shorter length (without telling you any warnings about it, of course).</p>
<p><strong>What needs to be done</strong>: Explicitly tell the user exactly how many characters they may use, and if the password is too long, <strong><em>tell them about it</em></strong>.</p>
<h2>Stupidity Award: access.enom.com</h2>
<p>If you change your password at this site, be extremely careful: DO NOT choose a password that is more than 30 characters long. When I changed my password to a 50-character long password, it happily accepted it. Unfortunately, the actual log-in interface only lets you type in 30 characters long! Since access.enom.com has no contact information, you&#8217;ll have to call someone somewhere somehow to sort out this mess.</p>
<h2>Realistic Outlook</h2>
<p>Legacy systems are really, really hard to migrate out of. My prediction is that the stupid, broken web interfaces will continue to thrive for at least 20 years. Why? It&#8217;s because people in 2031 will still be using passwords that are around 10 characters long with mostly alphanumeric symbols. Sure, web standards will have evolved by that time, but human brains will still be the same. The steady flow of 10-character passwords by the overwhelming majority of users will ensure that legacy systems remain competitive, at least when it comes to dealing with passwords.</p>
<p>Hopefully, by 2111, we&#8217;ll have sane password interfaces for all websites. Perhaps it will become a web standard by then, enforced by an international e-court, or some such.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/zuttobenkyou.wordpress.com/860/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/zuttobenkyou.wordpress.com/860/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/zuttobenkyou.wordpress.com/860/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/zuttobenkyou.wordpress.com/860/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/zuttobenkyou.wordpress.com/860/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/zuttobenkyou.wordpress.com/860/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/zuttobenkyou.wordpress.com/860/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/zuttobenkyou.wordpress.com/860/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/zuttobenkyou.wordpress.com/860/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/zuttobenkyou.wordpress.com/860/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/zuttobenkyou.wordpress.com/860/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/zuttobenkyou.wordpress.com/860/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/zuttobenkyou.wordpress.com/860/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/zuttobenkyou.wordpress.com/860/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=zuttobenkyou.wordpress.com&amp;blog=1384042&amp;post=860&amp;subd=zuttobenkyou&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://zuttobenkyou.wordpress.com/2011/10/03/website-passwords-a-big-mess/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">Shinobu</media:title>
		</media:content>
	</item>
	</channel>
</rss>
