<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-5463615614172154334</id><updated>2010-07-09T16:57:38.381-07:00</updated><title type='text'>Keith's Blog</title><subtitle type='html'>Software etc...</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://blog.keithsheppard.name/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5463615614172154334/posts/default'/><link rel='alternate' type='text/html' href='http://blog.keithsheppard.name/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Keith</name><uri>http://www.blogger.com/profile/04421311170476011799</uri><email>keithshep@gmail.com</email></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>19</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-5463615614172154334.post-2500765920013723880</id><published>2010-04-26T20:23:00.000-07:00</published><updated>2010-04-27T05:33:02.471-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='DVCS'/><category scheme='http://www.blogger.com/atom/ns#' term='darcs'/><title type='text'>Darcs: No server, no problem...</title><content type='html'>I was explaining to someone I work with today how darcs could be used for collaboration without having a public repo server. I haven't seen this work flow described elsewhere so maybe it will be useful to someone. There are a couple of reasons you might want to do version control sans-server.&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Your local IT department frowns upon setting up public facing servers and you have collaborators that live outside the firewall&lt;/li&gt;
&lt;li&gt;You don't have a spare server or domain name&lt;/li&gt;
&lt;li&gt;You just don't want to bother with it&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
But this approach still only makes sense if your code is private (if your code is open source the simplest thing for you to do is upload your repo to &lt;a href="http://patch-tag.com/"&gt;patch-tag&lt;/a&gt; and follow the typical work flow). For the purposes of this example let's imagine that Bill Gates and Steve Jobs want to collaborate on an Android App for finding and sharing info about yard sales.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Example Sans-Server Workflow:&lt;/b&gt;&lt;br /&gt;
Bill starts hacking on his app "YardSale!" and decides to track it with darcs:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;billg ~/yardsale_working&amp;gt; darcs init
billg ~/yardsale_working&amp;gt; darcs add --recursive *
billg ~/yardsale_working&amp;gt; darcs record --all&lt;/pre&gt;Bill tells Steve about his idea and Steve wants to help out so Bill creates a proxy repo which will act as a kind of stand-in for a public repository (it should be clear why soon). And sends the patches to Steve.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;billg ~/&amp;gt; mkdir yardsale_proxy
billg ~/&amp;gt; cd yardsale_proxy
billg ~/yardsale_proxy&amp;gt; darcs init
billg ~/yardsale_proxy&amp;gt; cd ../yardsale_working
billg ~/yardsale_working&amp;gt; darcs send --to=steve@apple.com --remote-repo=../yardsale_proxy
billg ~/yardsale_working&amp;gt; darcs push ../yardsale_proxy&lt;/pre&gt;&lt;br /&gt;
--remote-repo tells darcs to send any patches that are not in yardsale_proxy. Since yardsale_proxy is now empty, it mean send everything! Steve now needs to create his own proxy repo and apply Bill's patch file:&lt;br /&gt;
&lt;pre&gt;stevej ~/&amp;gt; mkdir yardsale_proxy
stevej ~/&amp;gt; cd yardsale_proxy
stevej ~/yardsale_proxy&amp;gt; darcs init
stevej ~/yardsale_proxy&amp;gt; darcs apply bills-initial-patches.dpatch
stevej ~/yardsale_proxy&amp;gt; cd ..
stevej ~/&amp;gt; darcs get yardsale_proxy yardsale_working&lt;/pre&gt;Steve works on integrating YardSale with google maps in his yardsale_working directory. When he's ready to share these changes with Bill he does something like:&lt;br /&gt;
&lt;pre&gt;stevej ~/yardsale_working&amp;gt; darcs record
stevej ~/yardsale_working&amp;gt; darcs send --to=bill@microsoft.com
stevej ~/yardsale_working&amp;gt; darcs push&lt;/pre&gt;The send command knows to compare yardsale_working to yardsale_proxy and prepare and send a patch email based on the differences it finds. Those differences are of course all of Steve's patches that Bill hasn't seen yet. Also notice that we didn't need to explicitly supply --remote-repo this time because darcs is smart enough to use the last "get" repo by default. The push just updates the proxy repo so that next time Steve does a send no old patches are sent again. Don't worry though. If you forget to push darcs is smart enough to ignore redundant patches.&lt;br /&gt;
&lt;br /&gt;
Bill gets the email and downloads the patch file. Now he needs to apply the patch file to the proxy repo and pull those changes into the working repo:&lt;br /&gt;
&lt;pre&gt;billg ~/yardsale_proxy&amp;gt; darcs apply google-maps-integration.dpatch
billg ~/yardsale_proxy&amp;gt; cd ../yardsale_working
billg ~/yardsale_working&amp;gt; darcs pull&lt;/pre&gt;Now Bill and Steve are synched up with the same patches. You can probably see how this would work pretty much the same way with a group of people sharing patches. darcs is cool!&lt;br /&gt;
&lt;br /&gt;
Edit: reworked the workflow (why do I always realize there is a better way after hitting Publish Post??)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5463615614172154334-2500765920013723880?l=blog.keithsheppard.name' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.keithsheppard.name/feeds/2500765920013723880/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.keithsheppard.name/2010/04/darcs-no-server-no-problem.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5463615614172154334/posts/default/2500765920013723880'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5463615614172154334/posts/default/2500765920013723880'/><link rel='alternate' type='text/html' href='http://blog.keithsheppard.name/2010/04/darcs-no-server-no-problem.html' title='Darcs: No server, no problem...'/><author><name>Keith</name><uri>http://www.blogger.com/profile/04421311170476011799</uri><email>keithshep@gmail.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='09087715816952529339'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5463615614172154334.post-4400823927153808804</id><published>2010-03-07T13:56:00.000-08:00</published><updated>2010-03-07T13:56:38.055-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><category scheme='http://www.blogger.com/atom/ns#' term='licensing'/><category scheme='http://www.blogger.com/atom/ns#' term='GPL'/><category scheme='http://www.blogger.com/atom/ns#' term='TxtSushi'/><category scheme='http://www.blogger.com/atom/ns#' term='BSD'/><title type='text'>TxtSushi 0.5.1</title><content type='html'>I've just released &lt;a href="http://hackage.haskell.org/package/txt-sushi"&gt;TxtSushi&lt;/a&gt; 0.5.1 on hackage. The only reason for this release was to update the license from GPL to BSD (there are no new features or fixes).&lt;br /&gt;
&lt;br /&gt;
I initially released under GPL because I wasn't really sure where I wanted to go with TxtSushi and I knew it was easier to go from GPL to a more liberal license than to move in the other direction. A recent &lt;a href="http://www.haskell.org/pipermail/haskell-cafe/2010-March/thread.html#74114"&gt;thread on licensing&lt;/a&gt; on haskell cafe convinced me that BSD is the way to go.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5463615614172154334-4400823927153808804?l=blog.keithsheppard.name' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.keithsheppard.name/feeds/4400823927153808804/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.keithsheppard.name/2010/03/txtsushi-051.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5463615614172154334/posts/default/4400823927153808804'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5463615614172154334/posts/default/4400823927153808804'/><link rel='alternate' type='text/html' href='http://blog.keithsheppard.name/2010/03/txtsushi-051.html' title='TxtSushi 0.5.1'/><author><name>Keith</name><uri>http://www.blogger.com/profile/04421311170476011799</uri><email>keithshep@gmail.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='09087715816952529339'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5463615614172154334.post-8573907734003737725</id><published>2010-01-30T15:36:00.000-08:00</published><updated>2010-01-31T17:19:54.328-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='TAO Framework'/><category scheme='http://www.blogger.com/atom/ns#' term='OpenGL'/><category scheme='http://www.blogger.com/atom/ns#' term='Mono'/><title type='text'>Getting Started with F# and OpenGL on OS X</title><content type='html'>&lt;p&gt;I am exploring programming with F# using mono on OS X. I'm thinking about using F# for a computer vision project and so I wanted to start out by playing around with some example code to see how it all works. This post is just to document the steps to get up and running. I'm a complete newbie to F# and mono so please give feedback if there are better ways to go about what I'm doing. I'd also like to eventually have a build environment that works well with mono on OS X and MS Visual Studio. Please leave a comment if you have any advice on this.&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;Initially I installed &lt;a href="http://www.go-mono.com/mono-downloads/download.html"&gt;mono&lt;/a&gt; 2.6.1. using the OS X Intel installer which worked without any issues.
&lt;/li&gt;
&lt;li&gt;Next I downloaded and unzipped the F# 1.9.7.8 zip file from the &lt;a href="http://research.microsoft.com/en-us/um/cambridge/projects/fsharp/release.aspx"&gt;F# downloads page&lt;/a&gt;. The zip file contains a convenient install-mono.sh script so all you have to do is &lt;pre&gt;chmod +x install-mono.sh &amp;amp;&amp;amp; sudo ./install-mono.sh&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;I decided to create bash scripts for the compiler and interpreter just for the sake of convenience:
&lt;pre&gt;mv FSharp-1.9.7.8 ~/bin/&lt;/pre&gt;
The contents of ~/bin/fsc.bash:
&lt;pre&gt;
#!/bin/bash

mono `dirname $0`/FSharp-1.9.7.8/bin/fsc.exe "$@"
&lt;/pre&gt;
... and likewise the contents of ~/fsi.bash:
&lt;pre&gt;
#!/bin/bash

mono `dirname $0`/FSharp-1.9.7.8/bin/fsi.exe "$@"
&lt;/pre&gt;&lt;/li&gt;&lt;/ul&gt;

&lt;p&gt;
OK great, we now have a working F# compiler and interpreter on OS X (make sure that ~/bin is on your $PATH though... you already knew that didn't you?). OK now let's try to work with one of the &lt;a href="http://code.msdn.microsoft.com/fsharpsamples"&gt;F# Samples&lt;/a&gt; to make sure things are working as expected. We can build and run the Samples101 example with this command:&lt;/p&gt;
&lt;pre&gt;fsc.bash --resource:SampleForm.resx \
    sample.fs sampleform.fs Beginners.fs \
    intermediate.fs program.fs
mono program.exe&lt;/pre&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_SB97tejMrX0/S2X1iOBaM3I/AAAAAAAAAHE/WRx85GAu8oQ/s1600-h/Picture+4.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 231px;" src="http://2.bp.blogspot.com/_SB97tejMrX0/S2X1iOBaM3I/AAAAAAAAAHE/WRx85GAu8oQ/s320/Picture+4.png" alt="" id="BLOGGER_PHOTO_ID_5433018493720736626" border="0" /&gt;&lt;/a&gt;

&lt;p&gt;
hmm... almost there. It looks like there is a hard-coded "..\..\" that gets inserted in source code path. A minor code change in sample.fs fixes that:
&lt;pre&gt;60c60,61
&amp;lt;         let dir = System.IO.Path.Combine(appdir, @"..\..\")
---
&amp;gt;         //let dir = System.IO.Path.Combine(appdir, @"..\..\")
&amp;gt;         let dir = appdir
&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;... now for a second try:&lt;/p&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_SB97tejMrX0/S2X61ZJeBwI/AAAAAAAAAHM/oqpoTlpSzW4/s1600-h/Picture+5.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 225px;" src="http://3.bp.blogspot.com/_SB97tejMrX0/S2X61ZJeBwI/AAAAAAAAAHM/oqpoTlpSzW4/s320/Picture+5.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5433024320682985218" /&gt;&lt;/a&gt;

&lt;p&gt;
That's better. OK, now let's see about getting an OpenGL example running.
&lt;ul&gt;
&lt;li&gt;First I downloaded the &lt;a href="http://www.taoframework.com/"&gt;TAO Framework&lt;/a&gt; (version 2.1.0) which provides .NET bindings to OpenGL and other API's for gaming&lt;/li&gt;
&lt;li&gt;You can install all of the libraries into mono's global registry by:
&lt;pre&gt;cd $TAO_HOME/bin
for file in *.dll; do sudo gacutil -i $file; done&lt;/pre&gt;
hmm, I get the following error while trying to install FreeType:
&lt;pre&gt;Installed Tao.FreeType.dll into the gac (/Library/Frameworks/Mono.framework/Versions/2.6.1/lib/mono/gac)

** (/Library/Frameworks/Mono.framework/Versions/2.6.1/lib/mono/2.0/gacutil.exe:253): WARNING **:
Error parsing  \x87\xa0: Error on line 3 char 46: 'libglfw.so"' is not a valid name: '"'&lt;/pre&gt;
I'm going to ignore this for now since it doesn't seem to matter for my simple example code.&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;

&lt;p&gt;
OK now let's translate the simplest TAO example to F# and see about getting it to run. This is a translation of the $TAO_HOME/source/examples/Redbook/Hello.cs example which is under the MIT License (see license text at end of post). Apologies for the lack of syntax hilighting:
&lt;pre&gt;open System
open Tao.FreeGlut
open Tao.OpenGl

let init () =
    // Select clearing color
    Gl.glClearColor (0.0f, 0.0f, 0.0f, 0.0f);

    // Initialize viewing values
    Gl.glMatrixMode Gl.GL_PROJECTION;
    Gl.glLoadIdentity ();
    Gl.glOrtho (0.0, 1.0, 0.0, 1.0, -1.0, 1.0)

let display () =
    // Clear all pixels.
    Gl.glClear Gl.GL_COLOR_BUFFER_BIT;

    // Draw white polygon (rectangle) with corners at (0.25, 0.25, 0.0) and
    // (0.75, 0.75, 0.0).
    Gl.glColor3f (1.0f, 1.0f, 1.0f);
    Gl.glBegin Gl.GL_POLYGON;
        Gl.glVertex3f (0.25f, 0.25f, 0.0f);
        Gl.glVertex3f (0.75f, 0.25f, 0.0f);
        Gl.glVertex3f (0.75f, 0.75f, 0.0f);
        Gl.glVertex3f (0.25f, 0.75f, 0.0f);
    Gl.glEnd ();

    // Don't wait!  Start processing buffered OpenGL routines.
    Gl.glFlush ()

// Declares initial window size, position, and display mode (single buffer and
// RGBA).  Open window with "Hello" in its title bar.  Call initialization
// routines.  Register callback function to display graphics.  Enter main loop
// and process events.
let main =
    Glut.glutInit ();
    Glut.glutInitDisplayMode (Glut.GLUT_SINGLE ||| Glut.GLUT_RGB);
    Glut.glutInitWindowSize (250, 250);
    Glut.glutInitWindowPosition (100, 100);
    ignore (Glut.glutCreateWindow "Hello");
    init ();
    Glut.glutDisplayFunc (new Glut.DisplayCallback(display));
    Glut.glutMainLoop ()

[&amp;lt;STAThread&amp;gt;]
do main&lt;/pre&gt;
&lt;/p&gt;

&lt;p&gt;
Now I just compile and run with:
&lt;pre&gt;fsc.bash -I $TAO_HOME/bin -r Tao.OpenGl.dll -r Tao.FreeGlut.dll hello.fs
mono hello.exe&lt;/pre&gt;
Hey, it works! That wasn't so painful.
&lt;/p&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_SB97tejMrX0/S2YqdYfOcQI/AAAAAAAAAHU/TXw1e3_pkDo/s1600-h/Picture+6.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 300px; height: 320px;" src="http://4.bp.blogspot.com/_SB97tejMrX0/S2YqdYfOcQI/AAAAAAAAAHU/TXw1e3_pkDo/s320/Picture+6.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5433076684747075842" /&gt;&lt;/a&gt;

&lt;p&gt;
MIT License
Copyright &lt;A9&gt;2003-2005 Tao Framework Team
http://www.taoframework.com
All rights reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5463615614172154334-8573907734003737725?l=blog.keithsheppard.name' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.keithsheppard.name/feeds/8573907734003737725/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.keithsheppard.name/2010/01/getting-started-with-f-and-opengl-on-os.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5463615614172154334/posts/default/8573907734003737725'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5463615614172154334/posts/default/8573907734003737725'/><link rel='alternate' type='text/html' href='http://blog.keithsheppard.name/2010/01/getting-started-with-f-and-opengl-on-os.html' title='Getting Started with F# and OpenGL on OS X'/><author><name>Keith</name><uri>http://www.blogger.com/profile/04421311170476011799</uri><email>keithshep@gmail.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='09087715816952529339'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_SB97tejMrX0/S2X1iOBaM3I/AAAAAAAAAHE/WRx85GAu8oQ/s72-c/Picture+4.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5463615614172154334.post-6444574329209494500</id><published>2010-01-10T10:13:00.000-08:00</published><updated>2010-01-10T20:13:56.500-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='site hosting'/><title type='text'>Simple Recipe for Static Domain Hosting With Blog</title><content type='html'>&lt;p&gt;Hopefully this post will help save someone time looking for similar hosting. My current hosting requirements are really simple. I need:&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;static site hosting for keithsheppard.name&lt;/li&gt;
&lt;li&gt;blog hosting for blog.keithsheppard.name&lt;/li&gt;&lt;/ul&gt;

&lt;p&gt;Luckily blogger lets you &lt;a href="http://www.google.com/support/blogger/bin/answer.py?hl=en&amp;answer=55373"&gt;use your own domain&lt;/a&gt; which takes care of blog.keithsheppard.name so I just need to find a static hosting service for keithsheppard.name. When I went looking for hosting though almost everything I found allows you to run at least PHP, perl scripts along with a MySQL database and costs $5/month or more. Using these hosts means that you have to pay for the extra resources, security and maintenance that goes along with allowing scripts and DB's to run. Another problem is that the cheaper services are often under-resourced which means added down time for you.&lt;/p&gt;

&lt;p&gt;After a lot of trial and error I found &lt;a href="http://he.net/web_hosting.html"&gt;hurricane electric&lt;/a&gt; $1/month static site hosting which has just worked with no problems worth mentioning. As a bonus you transfer your files using scp instead of the lame web-based file managers a lot of the other hosts make you use. HE also throws in a free domain name but I have my domain under dotster and I like keeping the hosting and name registration separate (that costs me an extra $15/year). So for what it's worth, this configuration has worked well for me.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5463615614172154334-6444574329209494500?l=blog.keithsheppard.name' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.keithsheppard.name/feeds/6444574329209494500/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.keithsheppard.name/2010/01/simple-recipe-for-static-domain-hosting.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5463615614172154334/posts/default/6444574329209494500'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5463615614172154334/posts/default/6444574329209494500'/><link rel='alternate' type='text/html' href='http://blog.keithsheppard.name/2010/01/simple-recipe-for-static-domain-hosting.html' title='Simple Recipe for Static Domain Hosting With Blog'/><author><name>Keith</name><uri>http://www.blogger.com/profile/04421311170476011799</uri><email>keithshep@gmail.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='09087715816952529339'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5463615614172154334.post-4361043716084842197</id><published>2009-12-30T17:09:00.000-08:00</published><updated>2010-01-02T09:44:45.209-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='TxtSushi'/><title type='text'>TxtSushi 0.5.0</title><content type='html'>&lt;p&gt;
Hiya! I've uploaded TxtSushi 0.5.0 to hackage and there are quite a few changes. I still need to update the documentation on &lt;a href="http://keithsheppard.name/txt-sushi"&gt;http://keithsheppard.name/txt-sushi&lt;/a&gt; which probably won't happen for a week or so (Edit: I have made the updates) but I'll summarize the changes here:
&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
generalized the JOIN syntax to allow joins on arbitrary expressions (you used to only be able to join on column IDs). &lt;a href="http://patch-tag.com/r/keithshep/txt-sushi/snapshot/hash/20091231012608-b3b23-dbe1eaf2453ae52f300a575889b3376f948cc887/content/pretty/tests/test16.1.bash"&gt;This test script&lt;/a&gt; demonstrates a join on a simple expression
&lt;/li&gt;

&lt;li&gt;
added initial support for the &amp;quot;for x in [col1 .. col4] yield expr&amp;quot; syntax. See this &lt;a href="http://patch-tag.com/r/keithshep/txt-sushi/snapshot/hash/20091221025954-b3b23-24f9d2fd8329e51817b61cc513af04b8376de248/content/pretty/tests/test12.1.bash"&gt;test script&lt;/a&gt; for an example of how it works.
&lt;/li&gt;

&lt;li&gt;
parser now works with UNIX, DOS and Mac newlines no matter which OS you're working on
&lt;/li&gt;

&lt;li&gt;
added integrated help for all SQL functions and operators. Use &amp;quot;tssql -help&amp;quot; to get a list of all functions and operators. Use &amp;quot;tssql -help sum&amp;quot; to get help on the SUM function
&lt;/li&gt;

&lt;li&gt;
added many new function definitions
&lt;/li&gt;

&lt;li&gt;
Grouped data syntax generalization: you can now group tables multiple times (via nested selects) and join those grouped tables to other tables (grouped or not). You can even mix grouped and non-grouped columns in the same expression.
&lt;/li&gt;

&lt;li&gt;
Many bug fixes and code simplifications (still learning how to write good Haskell). Most notably it is now very easy to extend tssql with new SQL functions and operations. Here's an example of what it takes to &lt;a href="http://patch-tag.com/r/keithshep/txt-sushi/snapshot/hash/20091231004744-b3b23-f4ecf1ba0edd45f220abb219bfb45ee6dc243598/patch"&gt;add a new function&lt;/a&gt;. 
&lt;/li&gt;

&lt;li&gt;
added support for bool constants in SQL expressions
&lt;/li&gt;

&lt;li&gt;
improved error messages
&lt;/li&gt;

&lt;li&gt;
&lt;a href="http://code.google.com/p/txt-sushi/issues/detail?id=7&amp;can=1"&gt;Declared&lt;/a&gt; the &amp;quot;classic join&amp;quot; performance bug a feature :-)
&lt;/li&gt;

&lt;li&gt;
and most importantly: changed the synopsis to &amp;quot;The SQL link in your *NIX chain&amp;quot;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Have fun and please send me your &lt;a href="http://code.google.com/p/txt-sushi/issues/list"&gt;bug reports&lt;/a&gt;!
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5463615614172154334-4361043716084842197?l=blog.keithsheppard.name' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.keithsheppard.name/feeds/4361043716084842197/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.keithsheppard.name/2009/12/txtsushi-050.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5463615614172154334/posts/default/4361043716084842197'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5463615614172154334/posts/default/4361043716084842197'/><link rel='alternate' type='text/html' href='http://blog.keithsheppard.name/2009/12/txtsushi-050.html' title='TxtSushi 0.5.0'/><author><name>Keith</name><uri>http://www.blogger.com/profile/04421311170476011799</uri><email>keithshep@gmail.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='09087715816952529339'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5463615614172154334.post-9080024345569233544</id><published>2009-10-04T14:39:00.000-07:00</published><updated>2009-10-04T18:12:31.626-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><category scheme='http://www.blogger.com/atom/ns#' term='TxtSushi'/><title type='text'>TxtSushi 0.4.0</title><content type='html'>&lt;p&gt;
I have just uploaded version 0.4.0 of &lt;a href="http://keithsheppard.name/txt-sushi/"&gt;TxtSushi&lt;/a&gt; with the following changes:
&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
General code improvements: fixed any -Wall warnings, improved help messages and
SQL parser error messages
&lt;/li&gt;
&lt;li&gt;
External sort: added an -external-sort option to tssql which tells tssql to
sort on disk for JOIN and ORDER BY. This was tested on a 10G CSV file and it
worked but it took 10 hours on my macbook! I think that this can be improved.
&lt;/li&gt;
&lt;li&gt;
Nested select: TxtSushi now allows nested select statements
&lt;/li&gt;
&lt;li&gt;
Classic joins: added support for older style join syntax. These joins are still
unoptimized though so their time complexity is row_count^2 rather than the
row_count(log row_count) complexity when using the INNER JOIN syntax
&lt;/li&gt;
&lt;li&gt;
Transpose utilities: added transposecsv and transposetab utilities which will
print a transposed version of the given table
&lt;/li&gt;
&lt;/ul&gt;

&lt;b&gt;&lt;u&gt;Fellow Haskell Hackers Wanted:&lt;/u&gt;&lt;/b&gt;

&lt;p&gt;
I think TxtSushi fills a unique niche in the *NIX toolchain for tabular data. On one side you have python, perl, sed, awk which are all great for general purpose text processing but it takes a lot of hand rolled code to do table filtering/joining/transformation and it gets even worse if you want to be able to deal correctly with quoted fields. On the other side you have real databases which allow you to do all of these things but which tend to be heavyweight solutions that require data import/export and don't want anything to do with your other *NIX commands. TxtSushi is different because it takes your text tables (or event STDIN) as they are and doesn't want to be anything more than an SQL link in your *NIX tool chain.
&lt;/p&gt;

&lt;p&gt;
The two long term goals that I have for TxtSushi are to improve it to the point where it is generally available on *NIX distro package managers and that it is "owned" and developed by a group instead of just me. These two goals really go together since I may not be able to do all of the work required to mature TxtSushi by myself. I thought about keeping these goals to myself until I could get to the point where the core architecture are solidified and the remaining work would amount to developing and integrating modules into the existing architecture, but I think things are just moving too slowly with me hacking alone. So, if you are interested in hacking TxtSushi with me let me know!
&lt;/p&gt;

&lt;p&gt;
Off the top of my head here are some relatively self-contained contributions that can be made:
&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
Extensible SQL Functions: This one should be fun. The way I implemented SQL functions isn't very pretty. The function parsers are in SQLParser.hs toward the bottom and the SQL Function execution code is in a big function guard toward the bottom of SQLExecution. I think we should have an SQLFunction type which would bundle up both the parsing and execution for a single SQL function/operator. The real payoff is that we could then have an XMonad-like configuration file that allows users to define their own SQL functions (Eg: if a user wants to define a STD_DEV aggregate function, now they can as long as they know haskell).
&lt;/li&gt;
&lt;li&gt;
Improve table parsing: the table parsing code was the first thing I wrote and it shows. It should be replaced by something more like what you find in &lt;a href="http://book.realworldhaskell.org/read/using-parsec.html"&gt;Real World Haskell: Using Parsec&lt;/a&gt; which is much more concise and tolerant of any end-of-line encoding
&lt;/li&gt;
&lt;li&gt;
External sort: The external sort algorithm works but could probably use a more expert Haskell hacker's eye to become efficient (there were a couple of reasons I couldn't just use the external-sort cabal library as-is).
&lt;/li&gt;
&lt;li&gt;
Efficient classic joins: The classic style WHERE joins should be just as efficient as the INNER JOIN joins. If you're interested in this I have an idea for what I think may be the easiest way to do this. See &lt;a href="http://code.google.com/p/txt-sushi/issues/detail?id=7"&gt;issue 7&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
Support for SQL Case Statement: See &lt;a href="http://code.google.com/p/txt-sushi/issues/detail?id=8"&gt;issue 8&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
Implement window functions like RANK() and ROW_NUMBER(): See &lt;a href="http://code.google.com/p/txt-sushi/issues/detail?id=11"&gt;issue 11&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
Test cases? Bug reports? Some other idea you have? ...
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
If you're curious, the simplest way to browse the source is here &lt;a href="http://patch-tag.com/r/keithshep/txt-sushi"&gt;http://patch-tag.com/r/keithshep/txt-sushi&lt;/a&gt;.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5463615614172154334-9080024345569233544?l=blog.keithsheppard.name' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.keithsheppard.name/feeds/9080024345569233544/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.keithsheppard.name/2009/10/txtsushi-040.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5463615614172154334/posts/default/9080024345569233544'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5463615614172154334/posts/default/9080024345569233544'/><link rel='alternate' type='text/html' href='http://blog.keithsheppard.name/2009/10/txtsushi-040.html' title='TxtSushi 0.4.0'/><author><name>Keith</name><uri>http://www.blogger.com/profile/04421311170476011799</uri><email>keithshep@gmail.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='09087715816952529339'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5463615614172154334.post-687766048641859812</id><published>2009-08-12T16:27:00.000-07:00</published><updated>2009-10-03T12:47:29.133-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='site hosting'/><title type='text'>Server Died!</title><content type='html'>&lt;p&gt;Update 1: The site is back up and running, hosted by izfree.com&lt;/p&gt;
&lt;p&gt;
Update 2: izfree.com was not working out so I switched to he.net. It's $1 per month
for static file hosting. I'll report back after a while on how he.net is going.
&lt;/p&gt;

&lt;p&gt;
I've been serving up my website on the cheap using dynamic DNS and a spare linux box at home. My linux box just died so my main site is down :-(. Luckily my blog is hosted by blogger which is why it's still doing fine.
&lt;/p&gt;

&lt;p&gt;
I don't suppose anyone knows a good way to host a web site for free assuming all the content is just static pages? I need to be able to come with my own domain name too. I think I'm going to have to finally break down and actually spend money on hosting.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5463615614172154334-687766048641859812?l=blog.keithsheppard.name' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.keithsheppard.name/feeds/687766048641859812/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.keithsheppard.name/2009/08/server-died.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5463615614172154334/posts/default/687766048641859812'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5463615614172154334/posts/default/687766048641859812'/><link rel='alternate' type='text/html' href='http://blog.keithsheppard.name/2009/08/server-died.html' title='Server Died!'/><author><name>Keith</name><uri>http://www.blogger.com/profile/04421311170476011799</uri><email>keithshep@gmail.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='09087715816952529339'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5463615614172154334.post-7347807459472099737</id><published>2009-07-22T20:31:00.001-07:00</published><updated>2009-07-22T20:45:49.001-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><category scheme='http://www.blogger.com/atom/ns#' term='linear algebra'/><category scheme='http://www.blogger.com/atom/ns#' term='BTTML'/><title type='text'>Bird Tracks Through Math Land: Gaussian Elimination with Back Substitution</title><content type='html'>&lt;p&gt;
Last time we went over some basic matrix operations and this time around we will
talk about how Gaussian Elimination works starting with the concepts and
working into an implementation using Haskell.
Gaussian Elimination is an algorithm that can be used to solve a system of
linear equations. Let's forget about matrices for a now and just get
a feel for how this works with equations. Suppose you are given the
following linear equations:
&lt;/p&gt;

&lt;pre&gt;2x  + y  + z = 1    (L1)
4x  + 3y + z = -1   (L2)
-2x + 2y + z = 7    (L3)&lt;/pre&gt;

&lt;p&gt;
and you are asked to solve for x, y and z. Gaussian Elimination gives you a
systematic way of finding the solution. The first major step is to "zero out" the
lower triangle of coefficients. So we want the x coefficient from L2
to be 0 and we want the x and y coefficients from L3 to be zero.
&lt;/p&gt;

&lt;p&gt;
Step 1.1: We'll zero out the x coefficient in L2 by subtracting 2*L1 giving us L2'
&lt;/p&gt;
&lt;pre&gt;0x + y - z = -3     (L2')&lt;/pre&gt;

&lt;p&gt;
Step 1.2: We can zero out x in L3 by adding L1 giving us L3'
&lt;/p&gt;
&lt;pre&gt;0x + 3y + 2z = 8    (L3')&lt;/pre&gt;

&lt;p&gt;
Step 1.3: Now we can eliminate y from our modified L3' by subtracting 3*L2' giving us L3'':
&lt;/p&gt;
&lt;pre&gt;0x + 0y + 5z = 17   (L3'')&lt;/pre&gt;

&lt;p&gt;
You'll notice a pattern forming by step 1.3. Because we're multiplying L3'
by a factor of L2' both of which already have 0x coefficients we can be sure
that any choice we make to zero out the y coefficient will still leave us
with a zero x coefficient in L3''.
&lt;/p&gt;

&lt;p&gt;
Lets take a look at our new equations in with zero coefficients under the
diagonal (triangular form)
&lt;/p&gt;
&lt;pre&gt;2x + y  + z  = 1    (L1)
0x + y  - z  = -3   (L2')
0x + 0y + 5z = 17   (L3'')&lt;/pre&gt;

&lt;p&gt;
OK cool, now we can use back substitution to solve for z, y and x (in that
order).
&lt;/p&gt;&lt;p&gt;

Step 2.1: Solve for z using L3''
&lt;/p&gt;&lt;pre&gt;5z = 17
z = 17/5 = 3.4&lt;/pre&gt;

&lt;p&gt;
Step 2.2: Solve for y using L2' and the value of z we just calculated
&lt;/p&gt;
&lt;pre&gt;y - 3.4 = -3
y = 0.4&lt;/pre&gt;

&lt;p&gt;
Step 2.3: Solve for z using L1 along with our calculated z and y:
&lt;/p&gt;
&lt;pre&gt;2x + 0.4 + 3.4 = 1
2x = -2.8
x = -1.4&lt;/pre&gt;

&lt;p&gt;
Done!
&lt;/p&gt;

&lt;p&gt;
Well, sort of... There is one special case that we need to account for. Let's
repeat our example after changing the x coefficient in L2 to 6.
&lt;/p&gt;

&lt;pre&gt;2x  + y  + z = 1    (M1)
6x  + 3y + z = -1   (M2)
-2x + 2y + z = 7    (M3)&lt;/pre&gt;

&lt;p&gt;
We can zero out the x column as before
&lt;/p&gt;

&lt;p&gt;
At this point we're supposed to zero out the y coefficient in M3' by adding
a multiple of M2' but that's not possible because the y coefficient in M2'
is 0. The way we fix this is by having one of the equations below M2' with a
non-zero y coefficient take M2's place. In this particular case M3' is our only
option.
&lt;/p&gt;

&lt;pre&gt;2x + y  + z  = 1    (M1)
0x + 3y + 2z = 8    (M3')
0x + 0y - 2z = -4   (M2')&lt;/pre&gt;

&lt;p&gt;
And after solving with back substitution:
&lt;/p&gt;
&lt;pre&gt;x = -7/6 = -1.1666
y = 4/3 = 1.333
z = 2&lt;/pre&gt;

&lt;p&gt;
In this case we're done diagonalizing the linear equations (if there were
more coefficients to zero out we would just keep going as before).
&lt;/p&gt;

&lt;pre&gt;&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt; &lt;span class="hs-keyword"&gt;module&lt;/span&gt; &lt;span class="hs-conid"&gt;Math&lt;/span&gt;&lt;span class="hs-varop"&gt;.&lt;/span&gt;&lt;span class="hs-conid"&gt;BTTML&lt;/span&gt;&lt;span class="hs-varop"&gt;.&lt;/span&gt;&lt;span class="hs-conid"&gt;GaussianElimination&lt;/span&gt; &lt;span class="hs-layout"&gt;(&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;   &lt;span class="hs-varid"&gt;solveWithGaussAndBackSub&lt;/span&gt;&lt;span class="hs-layout"&gt;,&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;   &lt;span class="hs-varid"&gt;gaussianElimination&lt;/span&gt;&lt;span class="hs-layout"&gt;,&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;   &lt;span class="hs-varid"&gt;backSubstituteUpper&lt;/span&gt;&lt;span class="hs-layout"&gt;,&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;   &lt;span class="hs-varid"&gt;backSubstituteLower&lt;/span&gt;&lt;span class="hs-layout"&gt;)&lt;/span&gt; &lt;span class="hs-keyword"&gt;where&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt; &lt;span class="hs-keyword"&gt;import&lt;/span&gt; &lt;span class="hs-conid"&gt;Data&lt;/span&gt;&lt;span class="hs-varop"&gt;.&lt;/span&gt;&lt;span class="hs-conid"&gt;List&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt; &lt;span class="hs-keyword"&gt;import&lt;/span&gt; &lt;span class="hs-conid"&gt;Math&lt;/span&gt;&lt;span class="hs-varop"&gt;.&lt;/span&gt;&lt;span class="hs-conid"&gt;BTTML&lt;/span&gt;&lt;span class="hs-varop"&gt;.&lt;/span&gt;&lt;span class="hs-conid"&gt;BasicMatrixOps&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;
One last thing before we move into code. All of the coefficients on the
left hand side will be represented as a matrix and all of the values on
the right hand side will be represented as a column vector. For example,
our initial system of linear equations (L1, L2 and L3) can be represented as:
&lt;/p&gt;

&lt;pre&gt;&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt; &lt;span class="hs-varid"&gt;lCoefMatrix&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;   &lt;span class="hs-keyglyph"&gt;[&lt;/span&gt;&lt;span class="hs-keyglyph"&gt;[&lt;/span&gt;&lt;span class="hs-num"&gt;2.0&lt;/span&gt;&lt;span class="hs-layout"&gt;,&lt;/span&gt;  &lt;span class="hs-num"&gt;1.0&lt;/span&gt;&lt;span class="hs-layout"&gt;,&lt;/span&gt; &lt;span class="hs-num"&gt;1.0&lt;/span&gt;&lt;span class="hs-keyglyph"&gt;]&lt;/span&gt;&lt;span class="hs-layout"&gt;,&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;    &lt;span class="hs-keyglyph"&gt;[&lt;/span&gt;&lt;span class="hs-num"&gt;4.0&lt;/span&gt;&lt;span class="hs-layout"&gt;,&lt;/span&gt;  &lt;span class="hs-num"&gt;3.0&lt;/span&gt;&lt;span class="hs-layout"&gt;,&lt;/span&gt; &lt;span class="hs-num"&gt;1.0&lt;/span&gt;&lt;span class="hs-keyglyph"&gt;]&lt;/span&gt;&lt;span class="hs-layout"&gt;,&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;    &lt;span class="hs-keyglyph"&gt;[&lt;/span&gt;&lt;span class="hs-comment"&gt;-&lt;/span&gt;&lt;span class="hs-num"&gt;2.0&lt;/span&gt;&lt;span class="hs-layout"&gt;,&lt;/span&gt; &lt;span class="hs-num"&gt;2.0&lt;/span&gt;&lt;span class="hs-layout"&gt;,&lt;/span&gt; &lt;span class="hs-num"&gt;1.0&lt;/span&gt;&lt;span class="hs-keyglyph"&gt;]&lt;/span&gt;&lt;span class="hs-keyglyph"&gt;]&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt; &lt;span class="hs-varid"&gt;lRHSVector&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;[&lt;/span&gt;&lt;span class="hs-num"&gt;1.0&lt;/span&gt;&lt;span class="hs-layout"&gt;,&lt;/span&gt; &lt;span class="hs-comment"&gt;-&lt;/span&gt;&lt;span class="hs-num"&gt;1.0&lt;/span&gt;&lt;span class="hs-layout"&gt;,&lt;/span&gt; &lt;span class="hs-num"&gt;7.0&lt;/span&gt;&lt;span class="hs-keyglyph"&gt;]&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;and our M equations can be represented as&lt;/p&gt;

&lt;pre&gt;&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt; &lt;span class="hs-varid"&gt;mCoefMatrix&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;   &lt;span class="hs-keyglyph"&gt;[&lt;/span&gt;&lt;span class="hs-keyglyph"&gt;[&lt;/span&gt;&lt;span class="hs-num"&gt;2.0&lt;/span&gt;&lt;span class="hs-layout"&gt;,&lt;/span&gt;  &lt;span class="hs-num"&gt;1.0&lt;/span&gt;&lt;span class="hs-layout"&gt;,&lt;/span&gt; &lt;span class="hs-num"&gt;1.0&lt;/span&gt;&lt;span class="hs-keyglyph"&gt;]&lt;/span&gt;&lt;span class="hs-layout"&gt;,&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;    &lt;span class="hs-keyglyph"&gt;[&lt;/span&gt;&lt;span class="hs-num"&gt;6.0&lt;/span&gt;&lt;span class="hs-layout"&gt;,&lt;/span&gt;  &lt;span class="hs-num"&gt;3.0&lt;/span&gt;&lt;span class="hs-layout"&gt;,&lt;/span&gt; &lt;span class="hs-num"&gt;1.0&lt;/span&gt;&lt;span class="hs-keyglyph"&gt;]&lt;/span&gt;&lt;span class="hs-layout"&gt;,&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;    &lt;span class="hs-keyglyph"&gt;[&lt;/span&gt;&lt;span class="hs-comment"&gt;-&lt;/span&gt;&lt;span class="hs-num"&gt;2.0&lt;/span&gt;&lt;span class="hs-layout"&gt;,&lt;/span&gt; &lt;span class="hs-num"&gt;2.0&lt;/span&gt;&lt;span class="hs-layout"&gt;,&lt;/span&gt; &lt;span class="hs-num"&gt;1.0&lt;/span&gt;&lt;span class="hs-keyglyph"&gt;]&lt;/span&gt;&lt;span class="hs-keyglyph"&gt;]&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt; &lt;span class="hs-varid"&gt;mRHSVector&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;[&lt;/span&gt;&lt;span class="hs-num"&gt;1.0&lt;/span&gt;&lt;span class="hs-layout"&gt;,&lt;/span&gt; &lt;span class="hs-comment"&gt;-&lt;/span&gt;&lt;span class="hs-num"&gt;1.0&lt;/span&gt;&lt;span class="hs-layout"&gt;,&lt;/span&gt; &lt;span class="hs-num"&gt;7.0&lt;/span&gt;&lt;span class="hs-keyglyph"&gt;]&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Now for the code to implement the concepts we just went over.&lt;/p&gt;

&lt;pre&gt;&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt; &lt;span class="hs-varid"&gt;solveWithGaussAndBackSub&lt;/span&gt; &lt;span class="hs-varid"&gt;coefMat&lt;/span&gt; &lt;span class="hs-varid"&gt;rhsVec&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;   &lt;span class="hs-keyword"&gt;let&lt;/span&gt; &lt;span class="hs-layout"&gt;(&lt;/span&gt;&lt;span class="hs-varid"&gt;gaussElimCoefMat&lt;/span&gt;&lt;span class="hs-layout"&gt;,&lt;/span&gt; &lt;span class="hs-varid"&gt;gaussElimRHSVec&lt;/span&gt;&lt;span class="hs-layout"&gt;)&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&lt;/span&gt; &lt;span class="hs-varid"&gt;gaussianElimination&lt;/span&gt; &lt;span class="hs-varid"&gt;coefMat&lt;/span&gt; &lt;span class="hs-varid"&gt;rhsVec&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;   &lt;span class="hs-keyword"&gt;in&lt;/span&gt; &lt;span class="hs-varid"&gt;backSubstituteUpper&lt;/span&gt; &lt;span class="hs-varid"&gt;gaussElimCoefMat&lt;/span&gt; &lt;span class="hs-varid"&gt;gaussElimRHSVec&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;
"zero out" the lower triangle of the given coefficient matrix using the
gaussian elimination method
&lt;/p&gt;

&lt;pre&gt;&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt; &lt;span class="hs-varid"&gt;gaussianElimination&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;::&lt;/span&gt; &lt;span class="hs-layout"&gt;(&lt;/span&gt;&lt;span class="hs-conid"&gt;Fractional&lt;/span&gt; &lt;span class="hs-varid"&gt;a&lt;/span&gt;&lt;span class="hs-layout"&gt;)&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;[&lt;/span&gt;&lt;span class="hs-keyglyph"&gt;[&lt;/span&gt;&lt;span class="hs-varid"&gt;a&lt;/span&gt;&lt;span class="hs-keyglyph"&gt;]&lt;/span&gt;&lt;span class="hs-keyglyph"&gt;]&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;[&lt;/span&gt;&lt;span class="hs-varid"&gt;a&lt;/span&gt;&lt;span class="hs-keyglyph"&gt;]&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="hs-layout"&gt;(&lt;/span&gt;&lt;span class="hs-keyglyph"&gt;[&lt;/span&gt;&lt;span class="hs-keyglyph"&gt;[&lt;/span&gt;&lt;span class="hs-varid"&gt;a&lt;/span&gt;&lt;span class="hs-keyglyph"&gt;]&lt;/span&gt;&lt;span class="hs-keyglyph"&gt;]&lt;/span&gt;&lt;span class="hs-layout"&gt;,&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;[&lt;/span&gt;&lt;span class="hs-varid"&gt;a&lt;/span&gt;&lt;span class="hs-keyglyph"&gt;]&lt;/span&gt;&lt;span class="hs-layout"&gt;)&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt; &lt;span class="hs-varid"&gt;gaussianElimination&lt;/span&gt; &lt;span class="hs-conid"&gt;[]&lt;/span&gt; &lt;span class="hs-conid"&gt;[]&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&lt;/span&gt; &lt;span class="hs-layout"&gt;(&lt;/span&gt;&lt;span class="hs-conid"&gt;[]&lt;/span&gt;&lt;span class="hs-layout"&gt;,&lt;/span&gt; &lt;span class="hs-conid"&gt;[]&lt;/span&gt;&lt;span class="hs-layout"&gt;)&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt; &lt;span class="hs-varid"&gt;gaussianElimination&lt;/span&gt; &lt;span class="hs-conid"&gt;[]&lt;/span&gt; &lt;span class="hs-keyword"&gt;_&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&lt;/span&gt; &lt;span class="hs-varid"&gt;error&lt;/span&gt; &lt;span class="hs-str"&gt;"Matrix row count is greater than right hand size!"&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt; &lt;span class="hs-varid"&gt;gaussianElimination&lt;/span&gt; &lt;span class="hs-keyword"&gt;_&lt;/span&gt; &lt;span class="hs-conid"&gt;[]&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&lt;/span&gt; &lt;span class="hs-varid"&gt;error&lt;/span&gt; &lt;span class="hs-str"&gt;"Matrix row count is less than right hand size!"&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt; &lt;span class="hs-varid"&gt;gaussianElimination&lt;/span&gt; &lt;span class="hs-varid"&gt;coefMat&lt;/span&gt; &lt;span class="hs-varid"&gt;rhsVec&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;   &lt;span class="hs-keyword"&gt;let&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;       &lt;span class="hs-comment"&gt;-- work on zeroing out the leftmost coefficients in all equations&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;       &lt;span class="hs-comment"&gt;-- below the top (ie below topCoefs). The ensureNonZeroFirstCoef&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;       &lt;span class="hs-comment"&gt;-- function helps us deal with the special case where the first&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;       &lt;span class="hs-comment"&gt;-- coefficient is zero&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;       &lt;span class="hs-layout"&gt;(&lt;/span&gt;&lt;span class="hs-varid"&gt;topCoefs&lt;/span&gt;&lt;span class="hs-conop"&gt;:&lt;/span&gt;&lt;span class="hs-varid"&gt;otherCoefsMat&lt;/span&gt;&lt;span class="hs-layout"&gt;,&lt;/span&gt; &lt;span class="hs-varid"&gt;topRHSVal&lt;/span&gt;&lt;span class="hs-conop"&gt;:&lt;/span&gt;&lt;span class="hs-varid"&gt;otherRHSVals&lt;/span&gt;&lt;span class="hs-layout"&gt;)&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;           &lt;span class="hs-varid"&gt;ensureNonZeroFirstCoef&lt;/span&gt; &lt;span class="hs-varid"&gt;coefMat&lt;/span&gt; &lt;span class="hs-varid"&gt;rhsVec&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;       &lt;span class="hs-varid"&gt;zeroOutFactors&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&lt;/span&gt; &lt;span class="hs-varid"&gt;map&lt;/span&gt; &lt;span class="hs-layout"&gt;(&lt;/span&gt;&lt;span class="hs-varid"&gt;zeroOutFactor&lt;/span&gt; &lt;span class="hs-varid"&gt;topCoefs&lt;/span&gt;&lt;span class="hs-layout"&gt;)&lt;/span&gt; &lt;span class="hs-varid"&gt;otherCoefsMat&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;      
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;       &lt;span class="hs-varid"&gt;zeroOutCoefMatAddends&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&lt;/span&gt; &lt;span class="hs-varid"&gt;zipWith&lt;/span&gt; &lt;span class="hs-layout"&gt;(&lt;/span&gt;&lt;span class="hs-varid"&gt;map&lt;/span&gt; &lt;span class="hs-varop"&gt;.&lt;/span&gt; &lt;span class="hs-layout"&gt;(&lt;/span&gt;&lt;span class="hs-varop"&gt;*&lt;/span&gt;&lt;span class="hs-layout"&gt;)&lt;/span&gt;&lt;span class="hs-layout"&gt;)&lt;/span&gt; &lt;span class="hs-varid"&gt;zeroOutFactors&lt;/span&gt; &lt;span class="hs-layout"&gt;(&lt;/span&gt;&lt;span class="hs-varid"&gt;repeat&lt;/span&gt; &lt;span class="hs-varid"&gt;topCoefs&lt;/span&gt;&lt;span class="hs-layout"&gt;)&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;       &lt;span class="hs-varid"&gt;zeroedOutCoefMat&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&lt;/span&gt; &lt;span class="hs-varid"&gt;otherCoefsMat&lt;/span&gt; &lt;span class="hs-varop"&gt;`matAdd`&lt;/span&gt; &lt;span class="hs-varid"&gt;zeroOutCoefMatAddends&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;      
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;       &lt;span class="hs-comment"&gt;-- now calculate the right-hand-side to go along with the&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;       &lt;span class="hs-comment"&gt;-- zeroed out coefficients&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;       &lt;span class="hs-varid"&gt;zeroOutRHSAddends&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&lt;/span&gt; &lt;span class="hs-varid"&gt;map&lt;/span&gt; &lt;span class="hs-layout"&gt;(&lt;/span&gt;&lt;span class="hs-varid"&gt;topRHSVal&lt;/span&gt; &lt;span class="hs-varop"&gt;*&lt;/span&gt;&lt;span class="hs-layout"&gt;)&lt;/span&gt; &lt;span class="hs-varid"&gt;zeroOutFactors&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;       &lt;span class="hs-varid"&gt;zeroedOutRHS&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&lt;/span&gt; &lt;span class="hs-varid"&gt;eqZipWith&lt;/span&gt; &lt;span class="hs-layout"&gt;(&lt;/span&gt;&lt;span class="hs-varop"&gt;+&lt;/span&gt;&lt;span class="hs-layout"&gt;)&lt;/span&gt; &lt;span class="hs-varid"&gt;otherRHSVals&lt;/span&gt; &lt;span class="hs-varid"&gt;zeroOutRHSAddends&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;      
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;       &lt;span class="hs-comment"&gt;-- now that the leftmost coefs are zero we need to recurse into the&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;       &lt;span class="hs-comment"&gt;-- lower right corner&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;       &lt;span class="hs-varid"&gt;lowerRightMat&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&lt;/span&gt; &lt;span class="hs-varid"&gt;map&lt;/span&gt; &lt;span class="hs-varid"&gt;tail&lt;/span&gt; &lt;span class="hs-varid"&gt;zeroedOutCoefMat&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;       &lt;span class="hs-layout"&gt;(&lt;/span&gt;&lt;span class="hs-varid"&gt;newLowerRightMat&lt;/span&gt;&lt;span class="hs-layout"&gt;,&lt;/span&gt; &lt;span class="hs-varid"&gt;newLowerRHS&lt;/span&gt;&lt;span class="hs-layout"&gt;)&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&lt;/span&gt; &lt;span class="hs-varid"&gt;gaussianElimination&lt;/span&gt; &lt;span class="hs-varid"&gt;lowerRightMat&lt;/span&gt; &lt;span class="hs-varid"&gt;zeroedOutRHS&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;      
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;       &lt;span class="hs-comment"&gt;-- OK now we just have to zip it up like we need&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;       &lt;span class="hs-varid"&gt;newLowerMat&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&lt;/span&gt; &lt;span class="hs-varid"&gt;eqZipWith&lt;/span&gt; &lt;span class="hs-layout"&gt;(&lt;/span&gt;&lt;span class="hs-conop"&gt;:&lt;/span&gt;&lt;span class="hs-layout"&gt;)&lt;/span&gt; &lt;span class="hs-layout"&gt;(&lt;/span&gt;&lt;span class="hs-varid"&gt;map&lt;/span&gt; &lt;span class="hs-varid"&gt;head&lt;/span&gt; &lt;span class="hs-varid"&gt;zeroedOutCoefMat&lt;/span&gt;&lt;span class="hs-layout"&gt;)&lt;/span&gt; &lt;span class="hs-varid"&gt;newLowerRightMat&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;       &lt;span class="hs-varid"&gt;finalMat&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&lt;/span&gt; &lt;span class="hs-varid"&gt;topCoefs&lt;/span&gt;&lt;span class="hs-conop"&gt;:&lt;/span&gt;&lt;span class="hs-varid"&gt;newLowerMat&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;       &lt;span class="hs-varid"&gt;finalRHS&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&lt;/span&gt; &lt;span class="hs-varid"&gt;topRHSVal&lt;/span&gt;&lt;span class="hs-conop"&gt;:&lt;/span&gt;&lt;span class="hs-varid"&gt;newLowerRHS&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;   &lt;span class="hs-keyword"&gt;in&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;       &lt;span class="hs-layout"&gt;(&lt;/span&gt;&lt;span class="hs-varid"&gt;finalMat&lt;/span&gt;&lt;span class="hs-layout"&gt;,&lt;/span&gt; &lt;span class="hs-varid"&gt;finalRHS&lt;/span&gt;&lt;span class="hs-layout"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;
this function is just to make sure that the left-most coefficient is
non-zero. If the 1st leftmost coefficient is zero in the 1st row then
that row is swapped with the 1st row that has a non-zero starting
coefficient
&lt;/p&gt;

&lt;pre&gt;&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt; &lt;span class="hs-varid"&gt;ensureNonZeroFirstCoef&lt;/span&gt; &lt;span class="hs-varid"&gt;coefMat&lt;/span&gt; &lt;span class="hs-varid"&gt;rhsVec&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;   &lt;span class="hs-keyword"&gt;let&lt;/span&gt; &lt;span class="hs-varid"&gt;firstNonZeroIndex&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&lt;/span&gt; &lt;span class="hs-varid"&gt;findIndex&lt;/span&gt; &lt;span class="hs-layout"&gt;(&lt;/span&gt;&lt;span class="hs-layout"&gt;(&lt;/span&gt;&lt;span class="hs-varop"&gt;/=&lt;/span&gt; &lt;span class="hs-num"&gt;0&lt;/span&gt;&lt;span class="hs-layout"&gt;)&lt;/span&gt; &lt;span class="hs-varop"&gt;.&lt;/span&gt; &lt;span class="hs-varid"&gt;head&lt;/span&gt;&lt;span class="hs-layout"&gt;)&lt;/span&gt; &lt;span class="hs-varid"&gt;coefMat&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;   &lt;span class="hs-keyword"&gt;in&lt;/span&gt; &lt;span class="hs-keyword"&gt;case&lt;/span&gt; &lt;span class="hs-varid"&gt;firstNonZeroIndex&lt;/span&gt; &lt;span class="hs-keyword"&gt;of&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;       &lt;span class="hs-conid"&gt;Just&lt;/span&gt; &lt;span class="hs-num"&gt;0&lt;/span&gt;  &lt;span class="hs-keyglyph"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="hs-layout"&gt;(&lt;/span&gt;&lt;span class="hs-varid"&gt;coefMat&lt;/span&gt;&lt;span class="hs-layout"&gt;,&lt;/span&gt; &lt;span class="hs-varid"&gt;rhsVec&lt;/span&gt;&lt;span class="hs-layout"&gt;)&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;       &lt;span class="hs-conid"&gt;Just&lt;/span&gt; &lt;span class="hs-varid"&gt;i&lt;/span&gt;  &lt;span class="hs-keyglyph"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="hs-layout"&gt;(&lt;/span&gt;&lt;span class="hs-varid"&gt;zeroSwap&lt;/span&gt; &lt;span class="hs-varid"&gt;i&lt;/span&gt; &lt;span class="hs-varid"&gt;coefMat&lt;/span&gt;&lt;span class="hs-layout"&gt;,&lt;/span&gt; &lt;span class="hs-varid"&gt;zeroSwap&lt;/span&gt; &lt;span class="hs-varid"&gt;i&lt;/span&gt; &lt;span class="hs-varid"&gt;rhsVec&lt;/span&gt;&lt;span class="hs-layout"&gt;)&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;       &lt;span class="hs-conid"&gt;Nothing&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="hs-varid"&gt;error&lt;/span&gt; &lt;span class="hs-str"&gt;"Failed to find non-zero coefficient!"&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;   &lt;span class="hs-keyword"&gt;where&lt;/span&gt; &lt;span class="hs-varid"&gt;zeroSwap&lt;/span&gt; &lt;span class="hs-varid"&gt;i&lt;/span&gt; &lt;span class="hs-varid"&gt;xs&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;           &lt;span class="hs-keyword"&gt;let&lt;/span&gt; &lt;span class="hs-layout"&gt;(&lt;/span&gt;&lt;span class="hs-varid"&gt;start&lt;/span&gt;&lt;span class="hs-layout"&gt;,&lt;/span&gt; &lt;span class="hs-varid"&gt;finish&lt;/span&gt;&lt;span class="hs-layout"&gt;)&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&lt;/span&gt; &lt;span class="hs-varid"&gt;splitAt&lt;/span&gt; &lt;span class="hs-varid"&gt;i&lt;/span&gt; &lt;span class="hs-varid"&gt;xs&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;           &lt;span class="hs-keyword"&gt;in&lt;/span&gt; &lt;span class="hs-varid"&gt;head&lt;/span&gt; &lt;span class="hs-varid"&gt;finish&lt;/span&gt; &lt;span class="hs-conop"&gt;:&lt;/span&gt; &lt;span class="hs-varid"&gt;start&lt;/span&gt; &lt;span class="hs-varop"&gt;++&lt;/span&gt; &lt;span class="hs-varid"&gt;tail&lt;/span&gt; &lt;span class="hs-varid"&gt;finish&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;
the zeroOutFactor determines the factor that should be used to zero out the
left-most coefficient in otherCoefs
&lt;/p&gt;

&lt;pre&gt;&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt; &lt;span class="hs-varid"&gt;zeroOutFactor&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;::&lt;/span&gt; &lt;span class="hs-layout"&gt;(&lt;/span&gt;&lt;span class="hs-conid"&gt;Fractional&lt;/span&gt; &lt;span class="hs-varid"&gt;a&lt;/span&gt;&lt;span class="hs-layout"&gt;)&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;[&lt;/span&gt;&lt;span class="hs-varid"&gt;a&lt;/span&gt;&lt;span class="hs-keyglyph"&gt;]&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;[&lt;/span&gt;&lt;span class="hs-varid"&gt;a&lt;/span&gt;&lt;span class="hs-keyglyph"&gt;]&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="hs-varid"&gt;a&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt; &lt;span class="hs-varid"&gt;zeroOutFactor&lt;/span&gt; &lt;span class="hs-varid"&gt;topCoefs&lt;/span&gt; &lt;span class="hs-varid"&gt;otherCoefs&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;   &lt;span class="hs-keyword"&gt;let&lt;/span&gt; &lt;span class="hs-varid"&gt;topLeftCoef&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&lt;/span&gt; &lt;span class="hs-varid"&gt;head&lt;/span&gt; &lt;span class="hs-varid"&gt;topCoefs&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;       &lt;span class="hs-varid"&gt;otherLeftCoef&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&lt;/span&gt; &lt;span class="hs-varid"&gt;head&lt;/span&gt; &lt;span class="hs-varid"&gt;otherCoefs&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;   &lt;span class="hs-keyword"&gt;in&lt;/span&gt; &lt;span class="hs-varid"&gt;negate&lt;/span&gt; &lt;span class="hs-varop"&gt;$&lt;/span&gt; &lt;span class="hs-varid"&gt;otherLeftCoef&lt;/span&gt; &lt;span class="hs-varop"&gt;/&lt;/span&gt; &lt;span class="hs-varid"&gt;topLeftCoef&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;
Solve a matrix that is zeroed out below the diagonal using
back substitution. This function delegates all the real work to
backSubstituteLower.
&lt;/p&gt;

&lt;pre&gt;&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt; &lt;span class="hs-varid"&gt;backSubstituteUpper&lt;/span&gt; &lt;span class="hs-varid"&gt;upperTriangleMat&lt;/span&gt; &lt;span class="hs-varid"&gt;rhsVec&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;   &lt;span class="hs-keyword"&gt;let&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;       &lt;span class="hs-comment"&gt;-- reverse column and row order in the matrix, then reverse the RHS&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;       &lt;span class="hs-varid"&gt;revMat&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&lt;/span&gt; &lt;span class="hs-varid"&gt;reverse&lt;/span&gt; &lt;span class="hs-varop"&gt;$&lt;/span&gt; &lt;span class="hs-varid"&gt;map&lt;/span&gt; &lt;span class="hs-varid"&gt;reverse&lt;/span&gt; &lt;span class="hs-varid"&gt;upperTriangleMat&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;       &lt;span class="hs-varid"&gt;revRHS&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&lt;/span&gt; &lt;span class="hs-varid"&gt;reverse&lt;/span&gt; &lt;span class="hs-varid"&gt;rhsVec&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;   &lt;span class="hs-keyword"&gt;in&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;       &lt;span class="hs-comment"&gt;-- now we can back substiture as a lower triangle matrix, reverse&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;       &lt;span class="hs-comment"&gt;-- the result and we're done!&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;       &lt;span class="hs-varid"&gt;reverse&lt;/span&gt; &lt;span class="hs-varop"&gt;$&lt;/span&gt; &lt;span class="hs-varid"&gt;backSubstituteLower&lt;/span&gt; &lt;span class="hs-varid"&gt;revMat&lt;/span&gt; &lt;span class="hs-varid"&gt;revRHS&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;
solve a matrix that is zeroed out above the diagonal using
back substitution
&lt;/p&gt;

&lt;pre&gt;&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt; &lt;span class="hs-varid"&gt;backSubstituteLower&lt;/span&gt; &lt;span class="hs-varid"&gt;lowerTriangleMat&lt;/span&gt; &lt;span class="hs-varid"&gt;rhsVec&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&lt;/span&gt; &lt;span class="hs-varid"&gt;go&lt;/span&gt; &lt;span class="hs-conid"&gt;[]&lt;/span&gt; &lt;span class="hs-varid"&gt;lowerTriangleMat&lt;/span&gt; &lt;span class="hs-varid"&gt;rhsVec&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;   &lt;span class="hs-keyword"&gt;where&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;       &lt;span class="hs-varid"&gt;go&lt;/span&gt; &lt;span class="hs-keyword"&gt;_&lt;/span&gt; &lt;span class="hs-conid"&gt;[]&lt;/span&gt; &lt;span class="hs-conid"&gt;[]&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&lt;/span&gt; &lt;span class="hs-conid"&gt;[]&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;       &lt;span class="hs-varid"&gt;go&lt;/span&gt; &lt;span class="hs-varid"&gt;prevSolutions&lt;/span&gt; &lt;span class="hs-layout"&gt;(&lt;/span&gt;&lt;span class="hs-varid"&gt;matHead&lt;/span&gt;&lt;span class="hs-conop"&gt;:&lt;/span&gt;&lt;span class="hs-varid"&gt;matTail&lt;/span&gt;&lt;span class="hs-layout"&gt;)&lt;/span&gt; &lt;span class="hs-layout"&gt;(&lt;/span&gt;&lt;span class="hs-varid"&gt;rhsHead&lt;/span&gt;&lt;span class="hs-conop"&gt;:&lt;/span&gt;&lt;span class="hs-varid"&gt;rhsTail&lt;/span&gt;&lt;span class="hs-layout"&gt;)&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;           &lt;span class="hs-keyword"&gt;let&lt;/span&gt; &lt;span class="hs-layout"&gt;(&lt;/span&gt;&lt;span class="hs-varid"&gt;solved&lt;/span&gt;&lt;span class="hs-layout"&gt;,&lt;/span&gt; &lt;span class="hs-layout"&gt;(&lt;/span&gt;&lt;span class="hs-varid"&gt;solveNow&lt;/span&gt;&lt;span class="hs-conop"&gt;:&lt;/span&gt;&lt;span class="hs-varid"&gt;solveLater&lt;/span&gt;&lt;span class="hs-layout"&gt;)&lt;/span&gt;&lt;span class="hs-layout"&gt;)&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&lt;/span&gt; &lt;span class="hs-varid"&gt;splitAt&lt;/span&gt; &lt;span class="hs-layout"&gt;(&lt;/span&gt;&lt;span class="hs-varid"&gt;length&lt;/span&gt; &lt;span class="hs-varid"&gt;prevSolutions&lt;/span&gt;&lt;span class="hs-layout"&gt;)&lt;/span&gt; &lt;span class="hs-varid"&gt;matHead&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;               &lt;span class="hs-varid"&gt;subRHS&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&lt;/span&gt; &lt;span class="hs-varid"&gt;rhsHead&lt;/span&gt; &lt;span class="hs-comment"&gt;-&lt;/span&gt; &lt;span class="hs-layout"&gt;(&lt;/span&gt;&lt;span class="hs-varid"&gt;sum&lt;/span&gt; &lt;span class="hs-varop"&gt;$&lt;/span&gt; &lt;span class="hs-varid"&gt;eqZipWith&lt;/span&gt; &lt;span class="hs-layout"&gt;(&lt;/span&gt;&lt;span class="hs-varop"&gt;*&lt;/span&gt;&lt;span class="hs-layout"&gt;)&lt;/span&gt; &lt;span class="hs-varid"&gt;prevSolutions&lt;/span&gt; &lt;span class="hs-varid"&gt;solved&lt;/span&gt;&lt;span class="hs-layout"&gt;)&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;               &lt;span class="hs-varid"&gt;solution&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&lt;/span&gt; &lt;span class="hs-varid"&gt;subRHS&lt;/span&gt; &lt;span class="hs-varop"&gt;/&lt;/span&gt; &lt;span class="hs-varid"&gt;solveNow&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;           &lt;span class="hs-keyword"&gt;in&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;               &lt;span class="hs-varid"&gt;solution&lt;/span&gt;&lt;span class="hs-conop"&gt;:&lt;/span&gt;&lt;span class="hs-layout"&gt;(&lt;/span&gt;&lt;span class="hs-varid"&gt;go&lt;/span&gt; &lt;span class="hs-layout"&gt;(&lt;/span&gt;&lt;span class="hs-varid"&gt;prevSolutions&lt;/span&gt; &lt;span class="hs-varop"&gt;++&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;[&lt;/span&gt;&lt;span class="hs-varid"&gt;solution&lt;/span&gt;&lt;span class="hs-keyglyph"&gt;]&lt;/span&gt;&lt;span class="hs-layout"&gt;)&lt;/span&gt; &lt;span class="hs-varid"&gt;matTail&lt;/span&gt; &lt;span class="hs-varid"&gt;rhsTail&lt;/span&gt;&lt;span class="hs-layout"&gt;)&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;       &lt;span class="hs-comment"&gt;-- If we haven't hit one of the previous patterns that means that the&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;       &lt;span class="hs-comment"&gt;-- row is different than the RHS count&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;       &lt;span class="hs-varid"&gt;go&lt;/span&gt; &lt;span class="hs-keyword"&gt;_&lt;/span&gt; &lt;span class="hs-keyword"&gt;_&lt;/span&gt; &lt;span class="hs-keyword"&gt;_&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&lt;/span&gt;
&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt;           &lt;span class="hs-varid"&gt;error&lt;/span&gt; &lt;span class="hs-str"&gt;"Matrix row count doesn't match right hand side length!"&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;That should do it. You can test it by invoking:&lt;/p&gt;

&lt;pre&gt;&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt; &lt;span class="hs-varid"&gt;test1&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&lt;/span&gt; &lt;span class="hs-varid"&gt;solveWithGaussAndBackSub&lt;/span&gt; &lt;span class="hs-varid"&gt;lCoefMatrix&lt;/span&gt; &lt;span class="hs-varid"&gt;lRHSVector&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;or&lt;/p&gt;

&lt;pre&gt;&lt;span class="hs-varop"&gt;&amp;gt;&lt;/span&gt; &lt;span class="hs-varid"&gt;test2&lt;/span&gt; &lt;span class="hs-keyglyph"&gt;=&lt;/span&gt; &lt;span class="hs-varid"&gt;solveWithGaussAndBackSub&lt;/span&gt; &lt;span class="hs-varid"&gt;mCoefMatrix&lt;/span&gt; &lt;span class="hs-varid"&gt;mRHSVector&lt;/span&gt;
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5463615614172154334-7347807459472099737?l=blog.keithsheppard.name' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.keithsheppard.name/feeds/7347807459472099737/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.keithsheppard.name/2009/07/bird-tracks-through-math-land-gaussian.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5463615614172154334/posts/default/7347807459472099737'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5463615614172154334/posts/default/7347807459472099737'/><link rel='alternate' type='text/html' href='http://blog.keithsheppard.name/2009/07/bird-tracks-through-math-land-gaussian.html' title='Bird Tracks Through Math Land: Gaussian Elimination with Back Substitution'/><author><name>Keith</name><uri>http://www.blogger.com/profile/04421311170476011799</uri><email>keithshep@gmail.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='09087715816952529339'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5463615614172154334.post-4593499907622283463</id><published>2009-06-19T18:24:00.000-07:00</published><updated>2009-06-19T18:32:34.186-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MySQL'/><category scheme='http://www.blogger.com/atom/ns#' term='flat file'/><category scheme='http://www.blogger.com/atom/ns#' term='TxtSushi'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><title type='text'>MySQL responds to the threat ...</title><content type='html'>... posed by TxtSushi's foray into the database market.

&lt;a href="http://dev.mysql.com/tech-resources/articles/csv-storage-engine.html"&gt;http://dev.mysql.com/tech-resources/articles/csv-storage-engine.html&lt;/a&gt;

In all seriousness, this looks like a really nice solution.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5463615614172154334-4593499907622283463?l=blog.keithsheppard.name' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.keithsheppard.name/feeds/4593499907622283463/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.keithsheppard.name/2009/06/mysql-responds-to-threat.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5463615614172154334/posts/default/4593499907622283463'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5463615614172154334/posts/default/4593499907622283463'/><link rel='alternate' type='text/html' href='http://blog.keithsheppard.name/2009/06/mysql-responds-to-threat.html' title='MySQL responds to the threat ...'/><author><name>Keith</name><uri>http://www.blogger.com/profile/04421311170476011799</uri><email>keithshep@gmail.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='09087715816952529339'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5463615614172154334.post-1192615190536412718</id><published>2009-06-18T19:13:00.000-07:00</published><updated>2009-06-18T19:46:15.507-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='flat file'/><category scheme='http://www.blogger.com/atom/ns#' term='TxtSushi'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><title type='text'>HaExcel</title><content type='html'>I stumbled upon &lt;a href="http://haskell.di.uminho.pt/jacome/index.html"&gt;HaExcel&lt;/a&gt; today and thought "Wow! This sounds just like TxtSushi." After looking more closely it has important differences. It handles import/export between spreadsheet and database rather than directly performing queries on a set of spreadsheets.

If you're looking to bridge spreadsheets and databases you should give it a look.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5463615614172154334-1192615190536412718?l=blog.keithsheppard.name' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.keithsheppard.name/feeds/1192615190536412718/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.keithsheppard.name/2009/06/haexcel.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5463615614172154334/posts/default/1192615190536412718'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5463615614172154334/posts/default/1192615190536412718'/><link rel='alternate' type='text/html' href='http://blog.keithsheppard.name/2009/06/haexcel.html' title='HaExcel'/><author><name>Keith</name><uri>http://www.blogger.com/profile/04421311170476011799</uri><email>keithshep@gmail.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='09087715816952529339'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5463615614172154334.post-16744753068409426</id><published>2009-06-13T06:25:00.000-07:00</published><updated>2009-06-13T06:32:47.010-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='BTTML'/><title type='text'>BTTML repository</title><content type='html'>The BTTML darcs repository is up at &lt;a href="http://patch-tag.com/r/bird-tracks-through-math-land/home"&gt;http://patch-tag.com/r/bird-tracks-through-math-land/home&lt;/a&gt;

The blog posts will be updated with the latest version if I make any changes, but you can browse the entire history of modifications from the repository.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5463615614172154334-16744753068409426?l=blog.keithsheppard.name' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.keithsheppard.name/feeds/16744753068409426/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.keithsheppard.name/2009/06/bttml-repository.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5463615614172154334/posts/default/16744753068409426'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5463615614172154334/posts/default/16744753068409426'/><link rel='alternate' type='text/html' href='http://blog.keithsheppard.name/2009/06/bttml-repository.html' title='BTTML repository'/><author><name>Keith</name><uri>http://www.blogger.com/profile/04421311170476011799</uri><email>keithshep@gmail.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='09087715816952529339'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5463615614172154334.post-9153798258670359213</id><published>2009-06-12T17:28:00.000-07:00</published><updated>2009-07-05T15:21:20.437-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='literate programming'/><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><category scheme='http://www.blogger.com/atom/ns#' term='linear algebra'/><category scheme='http://www.blogger.com/atom/ns#' term='BTTML'/><title type='text'>Bird Tracks Through Math Land: Basic Matrix Ops</title><content type='html'>&lt;p&gt;
    Math scares me. I hate to say it but it's true. I write software for super smart
    researchers/scientists who are solving all kinds of complex mathematical
    problems in computational biology and this discomfort with math has become a
    limiting factor in how much I can contribute to my group. So, it's time for me
    to fix the problem.
&lt;/p&gt;

&lt;p&gt;
    I don't learn well from just reading books on math. I have tried it. I always
    end up feeling like I've learned a few rules but I don't really "get" it. I need
    to have a sense for what the small pieces are and how they all fit together to
    form a larger system of high level abstractions/components. That's how I build
    software and it's the only way I am comfortable learning technical concepts.
    That's what got me thinking about "Bird tracks through math land". The best way
    for me to learn these concepts is to implement them! If you build something you
    have to understand it right?
&lt;/p&gt;

&lt;p&gt;
    I am going to do this is through a series of Literate Haskell posts. The idea is
    to take it slow, and explain things as fully as possible. In the end I would
    like to have a series of math tutorials/notes that also happen to be working
    code (there is nothing like a compiler to keep you honest!). My hope is that
    these notes will be almost completely self contained and should not require
    much more math background than what you get in high school. You will have to be
    able to read the Haskell source code though (I very highly recommend
    &lt;a href="http://learnyouahaskell.com/"&gt;Learn You a Haskell&lt;/a&gt; for anyone new to
    Haskell).
&lt;/p&gt;

&lt;p&gt;
    A bit of background for anyone who is not familiar with Literate Haskell:
    &lt;ul&gt;
        &lt;li&gt;
           Haskell is a purely functional programming language. That means that the
           output of a function is completely determined by it's inputs (no cheating
           with hidden state). This aligns closely with the mathematical definition of
           a function, but is very different from main stream languages like C, Java ...
        &lt;/li&gt;
        &lt;li&gt;
           Haskell's syntax is beautiful and extremely concise. It is nearly as
           expressive as writing pseudocode which makes it perfect for this kind of
           project.
        &lt;/li&gt;
        &lt;li&gt;
           You're reading Literate Haskell source right now... Seriously! I wouldn't lie
           to you. Literate Haskell treats every line that does not start with a '&gt;'
           (AKA bird track) as a comment. Quoting Dr. Knuth, who conceived Literate
           Programming "The main idea is to regard a program as a communication to human
           beings rather than as a set of instructions to a computer." Which is exactly
           what I am trying to achieve.
        &lt;/li&gt;
    &lt;/ul&gt;
&lt;/p&gt;

&lt;p&gt;
    The road map for "Bird tracks through math land" is still up in the air, but for
    now I plan on starting with Linear Algebra and working my way toward its
    applications in probability and statistics. I am also very interested in
    Bayesian Networks so I think we'll head down that path later.
&lt;/p&gt;

&lt;p&gt;
    OK, enough blah blah. Lets get to it. We'll start by defining the basic matrix
    operations.
&lt;/p&gt;

&lt;pre&gt;&lt;span class='hs-varop'&gt;&amp;gt;&lt;/span&gt; &lt;span class='hs-keyword'&gt;module&lt;/span&gt; &lt;span class='hs-conid'&gt;Math&lt;/span&gt;&lt;span class='hs-varop'&gt;.&lt;/span&gt;&lt;span class='hs-conid'&gt;BTTML&lt;/span&gt;&lt;span class='hs-varop'&gt;.&lt;/span&gt;&lt;span class='hs-conid'&gt;BasicMatrixOps&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;
&lt;span class='hs-varop'&gt;&amp;gt;&lt;/span&gt;           &lt;span class='hs-varid'&gt;dotProd&lt;/span&gt;&lt;span class='hs-layout'&gt;,&lt;/span&gt;
&lt;span class='hs-varop'&gt;&amp;gt;&lt;/span&gt;           &lt;span class='hs-varid'&gt;matAdd&lt;/span&gt;&lt;span class='hs-layout'&gt;,&lt;/span&gt;
&lt;span class='hs-varop'&gt;&amp;gt;&lt;/span&gt;           &lt;span class='hs-varid'&gt;matSub&lt;/span&gt;&lt;span class='hs-layout'&gt;,&lt;/span&gt;
&lt;span class='hs-varop'&gt;&amp;gt;&lt;/span&gt;           &lt;span class='hs-varid'&gt;matMult&lt;/span&gt;&lt;span class='hs-layout'&gt;,&lt;/span&gt;
&lt;span class='hs-varop'&gt;&amp;gt;&lt;/span&gt;           &lt;span class='hs-varid'&gt;eqZipWith&lt;/span&gt;&lt;span class='hs-layout'&gt;,&lt;/span&gt;
&lt;span class='hs-varop'&gt;&amp;gt;&lt;/span&gt;           &lt;span class='hs-varid'&gt;matZipWith&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt; &lt;span class='hs-keyword'&gt;where&lt;/span&gt;
&lt;span class='hs-varop'&gt;&amp;gt;&lt;/span&gt;
&lt;span class='hs-varop'&gt;&amp;gt;&lt;/span&gt; &lt;span class='hs-keyword'&gt;import&lt;/span&gt; &lt;span class='hs-conid'&gt;Data&lt;/span&gt;&lt;span class='hs-varop'&gt;.&lt;/span&gt;&lt;span class='hs-conid'&gt;List&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;
    Let's define our own version of Haskell's zipWith function. eqZipWith applies
    the function f to each corresponding pair of elements in the given lists.
    It is just like Haskell's version, except that eqZipWith requires that the list
    lengths match.
&lt;/p&gt;

&lt;pre&gt;&lt;span class='hs-varop'&gt;&amp;gt;&lt;/span&gt; &lt;span class='hs-varid'&gt;eqZipWith&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;::&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-varid'&gt;a&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='hs-varid'&gt;b&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='hs-varid'&gt;c&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;[&lt;/span&gt;&lt;span class='hs-varid'&gt;a&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;]&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;[&lt;/span&gt;&lt;span class='hs-varid'&gt;b&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;]&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;[&lt;/span&gt;&lt;span class='hs-varid'&gt;c&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;]&lt;/span&gt;
&lt;span class='hs-varop'&gt;&amp;gt;&lt;/span&gt; &lt;span class='hs-varid'&gt;eqZipWith&lt;/span&gt; &lt;span class='hs-varid'&gt;f&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-varid'&gt;x&lt;/span&gt;&lt;span class='hs-conop'&gt;:&lt;/span&gt;&lt;span class='hs-varid'&gt;xs&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-varid'&gt;y&lt;/span&gt;&lt;span class='hs-conop'&gt;:&lt;/span&gt;&lt;span class='hs-varid'&gt;ys&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-varid'&gt;f&lt;/span&gt; &lt;span class='hs-varid'&gt;x&lt;/span&gt; &lt;span class='hs-varid'&gt;y&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt;&lt;span class='hs-conop'&gt;:&lt;/span&gt;&lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-varid'&gt;eqZipWith&lt;/span&gt; &lt;span class='hs-varid'&gt;f&lt;/span&gt; &lt;span class='hs-varid'&gt;xs&lt;/span&gt; &lt;span class='hs-varid'&gt;ys&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt;
&lt;span class='hs-varop'&gt;&amp;gt;&lt;/span&gt; &lt;span class='hs-varid'&gt;eqZipWith&lt;/span&gt; &lt;span class='hs-varid'&gt;f&lt;/span&gt; &lt;span class='hs-conid'&gt;[]&lt;/span&gt; &lt;span class='hs-conid'&gt;[]&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt; &lt;span class='hs-conid'&gt;[]&lt;/span&gt;
&lt;span class='hs-varop'&gt;&amp;gt;&lt;/span&gt; &lt;span class='hs-varid'&gt;eqZipWith&lt;/span&gt; &lt;span class='hs-keyword'&gt;_&lt;/span&gt; &lt;span class='hs-keyword'&gt;_&lt;/span&gt; &lt;span class='hs-keyword'&gt;_&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt; &lt;span class='hs-varid'&gt;error&lt;/span&gt; &lt;span class='hs-str'&gt;"List sizes should match"&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;
    Now let's create a matrix zip function. It applies a function f to each pair of
    corresponding elements from the two given matrices.
&lt;/p&gt;

&lt;pre&gt;&lt;span class='hs-varop'&gt;&amp;gt;&lt;/span&gt; &lt;span class='hs-varid'&gt;matZipWith&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;::&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-varid'&gt;a&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='hs-varid'&gt;b&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='hs-varid'&gt;c&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;[&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;[&lt;/span&gt;&lt;span class='hs-varid'&gt;a&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;]&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;]&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;[&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;[&lt;/span&gt;&lt;span class='hs-varid'&gt;b&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;]&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;]&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;[&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;[&lt;/span&gt;&lt;span class='hs-varid'&gt;c&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;]&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;]&lt;/span&gt;
&lt;span class='hs-varop'&gt;&amp;gt;&lt;/span&gt; &lt;span class='hs-varid'&gt;matZipWith&lt;/span&gt; &lt;span class='hs-varid'&gt;f&lt;/span&gt; &lt;span class='hs-varid'&gt;matrix1&lt;/span&gt; &lt;span class='hs-varid'&gt;matrix2&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt; &lt;span class='hs-varid'&gt;eqZipWith&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-varid'&gt;eqZipWith&lt;/span&gt; &lt;span class='hs-varid'&gt;f&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt; &lt;span class='hs-varid'&gt;matrix1&lt;/span&gt; &lt;span class='hs-varid'&gt;matrix2&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;
    Subtract matrix1 from matrix2. This is an element-by-element operation.
&lt;/p&gt;

&lt;pre&gt;&lt;span class='hs-varop'&gt;&amp;gt;&lt;/span&gt; &lt;span class='hs-varid'&gt;matSub&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;::&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-conid'&gt;Num&lt;/span&gt; &lt;span class='hs-varid'&gt;a&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;[&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;[&lt;/span&gt;&lt;span class='hs-varid'&gt;a&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;]&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;]&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;[&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;[&lt;/span&gt;&lt;span class='hs-varid'&gt;a&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;]&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;]&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;[&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;[&lt;/span&gt;&lt;span class='hs-varid'&gt;a&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;]&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;]&lt;/span&gt;
&lt;span class='hs-varop'&gt;&amp;gt;&lt;/span&gt; &lt;span class='hs-varid'&gt;matSub&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt; &lt;span class='hs-varid'&gt;matZipWith&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-comment'&gt;-&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;
    Matrix addition works pretty much the same way
&lt;/p&gt;

&lt;pre&gt;&lt;span class='hs-varop'&gt;&amp;gt;&lt;/span&gt; &lt;span class='hs-varid'&gt;matAdd&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;::&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-conid'&gt;Num&lt;/span&gt; &lt;span class='hs-varid'&gt;a&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;[&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;[&lt;/span&gt;&lt;span class='hs-varid'&gt;a&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;]&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;]&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;[&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;[&lt;/span&gt;&lt;span class='hs-varid'&gt;a&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;]&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;]&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;[&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;[&lt;/span&gt;&lt;span class='hs-varid'&gt;a&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;]&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;]&lt;/span&gt;
&lt;span class='hs-varop'&gt;&amp;gt;&lt;/span&gt; &lt;span class='hs-varid'&gt;matAdd&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt; &lt;span class='hs-varid'&gt;matZipWith&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-varop'&gt;+&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;
    The dot product (AKA scalar product) of two vectors is the sum of the product
    of each pair of elements. The vectors have to be the same length.
&lt;/p&gt;

&lt;pre&gt;&lt;span class='hs-varop'&gt;&amp;gt;&lt;/span&gt; &lt;span class='hs-varid'&gt;dotProd&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;::&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-conid'&gt;Num&lt;/span&gt; &lt;span class='hs-varid'&gt;a&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;[&lt;/span&gt;&lt;span class='hs-varid'&gt;a&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;]&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;[&lt;/span&gt;&lt;span class='hs-varid'&gt;a&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;]&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='hs-varid'&gt;a&lt;/span&gt;
&lt;span class='hs-varop'&gt;&amp;gt;&lt;/span&gt; &lt;span class='hs-varid'&gt;dotProd&lt;/span&gt; &lt;span class='hs-varid'&gt;vector1&lt;/span&gt; &lt;span class='hs-varid'&gt;vector2&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt; &lt;span class='hs-varid'&gt;sum&lt;/span&gt; &lt;span class='hs-varop'&gt;$&lt;/span&gt; &lt;span class='hs-varid'&gt;eqZipWith&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-varop'&gt;*&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt; &lt;span class='hs-varid'&gt;vector1&lt;/span&gt; &lt;span class='hs-varid'&gt;vector2&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;
    The wikipedia page on matrix multiplication is great:
    http://en.wikipedia.org/wiki/Matrix_multiplication
&lt;/p&gt;

&lt;p&gt;
    The most important points are:
    &lt;ul&gt;
        &lt;li&gt;
           The row count of the resulting matrix is determined by the row count of
           matrix1
        &lt;/li&gt;
        &lt;li&gt;
           The column count of the resulting matrix is determined by the column count
           of matrix2
        &lt;/li&gt;
        &lt;li&gt;
           element[i, j] of the resulting matrix is calculated by taking the dot product
           of the ith row vector from matrix1 and the jth column from matrix2. Also note
           that because these two vectors must be the same length, the column count of
           matrix1 must equal the row count of matrix2.
        &lt;/li&gt;
    &lt;/ul&gt;
&lt;/p&gt;

&lt;p&gt;
    In order to simplify the matrix multiplication we will transpose matrix2
    (moves element[i,j] to element[j,i]) which makes it trivial to pull out column
    vectors
&lt;/p&gt;

&lt;pre&gt;&lt;span class='hs-varop'&gt;&amp;gt;&lt;/span&gt; &lt;span class='hs-varid'&gt;matMult&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;::&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-conid'&gt;Num&lt;/span&gt; &lt;span class='hs-varid'&gt;a&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;[&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;[&lt;/span&gt;&lt;span class='hs-varid'&gt;a&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;]&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;]&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;[&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;[&lt;/span&gt;&lt;span class='hs-varid'&gt;a&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;]&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;]&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;[&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;[&lt;/span&gt;&lt;span class='hs-varid'&gt;a&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;]&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;]&lt;/span&gt;
&lt;span class='hs-varop'&gt;&amp;gt;&lt;/span&gt; &lt;span class='hs-varid'&gt;matMult&lt;/span&gt; &lt;span class='hs-varid'&gt;matrix1&lt;/span&gt; &lt;span class='hs-varid'&gt;matrix2&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt;
&lt;span class='hs-varop'&gt;&amp;gt;&lt;/span&gt;   &lt;span class='hs-keyglyph'&gt;[&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;[&lt;/span&gt;&lt;span class='hs-varid'&gt;row1&lt;/span&gt; &lt;span class='hs-varop'&gt;`dotProd`&lt;/span&gt; &lt;span class='hs-varid'&gt;col2&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;|&lt;/span&gt; &lt;span class='hs-varid'&gt;col2&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;&amp;lt;-&lt;/span&gt; &lt;span class='hs-varid'&gt;transpose&lt;/span&gt; &lt;span class='hs-varid'&gt;matrix2&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;]&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;|&lt;/span&gt; &lt;span class='hs-varid'&gt;row1&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;&amp;lt;-&lt;/span&gt; &lt;span class='hs-varid'&gt;matrix1&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;]&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;
    Now here are a couple of tests:
&lt;/p&gt;

&lt;pre&gt;&lt;span class='hs-varop'&gt;&amp;gt;&lt;/span&gt; &lt;span class='hs-varid'&gt;testMat1&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt;
&lt;span class='hs-varop'&gt;&amp;gt;&lt;/span&gt;   &lt;span class='hs-keyglyph'&gt;[&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;[&lt;/span&gt;&lt;span class='hs-num'&gt;2&lt;/span&gt;&lt;span class='hs-layout'&gt;,&lt;/span&gt; &lt;span class='hs-num'&gt;0&lt;/span&gt;&lt;span class='hs-layout'&gt;,&lt;/span&gt; &lt;span class='hs-comment'&gt;-&lt;/span&gt;&lt;span class='hs-num'&gt;1&lt;/span&gt;&lt;span class='hs-layout'&gt;,&lt;/span&gt; &lt;span class='hs-num'&gt;1&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;]&lt;/span&gt;&lt;span class='hs-layout'&gt;,&lt;/span&gt;
&lt;span class='hs-varop'&gt;&amp;gt;&lt;/span&gt;    &lt;span class='hs-keyglyph'&gt;[&lt;/span&gt;&lt;span class='hs-num'&gt;1&lt;/span&gt;&lt;span class='hs-layout'&gt;,&lt;/span&gt; &lt;span class='hs-num'&gt;2&lt;/span&gt;&lt;span class='hs-layout'&gt;,&lt;/span&gt; &lt;span class='hs-num'&gt;0&lt;/span&gt;&lt;span class='hs-layout'&gt;,&lt;/span&gt; &lt;span class='hs-num'&gt;1&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;]&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;]&lt;/span&gt;
&lt;/pre&gt;
&lt;pre&gt;&lt;span class='hs-varop'&gt;&amp;gt;&lt;/span&gt; &lt;span class='hs-varid'&gt;testMat2&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt;
&lt;span class='hs-varop'&gt;&amp;gt;&lt;/span&gt;   &lt;span class='hs-keyglyph'&gt;[&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;[&lt;/span&gt;&lt;span class='hs-num'&gt;1&lt;/span&gt;&lt;span class='hs-layout'&gt;,&lt;/span&gt; &lt;span class='hs-num'&gt;5&lt;/span&gt;&lt;span class='hs-layout'&gt;,&lt;/span&gt; &lt;span class='hs-comment'&gt;-&lt;/span&gt;&lt;span class='hs-num'&gt;7&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;]&lt;/span&gt;&lt;span class='hs-layout'&gt;,&lt;/span&gt;
&lt;span class='hs-varop'&gt;&amp;gt;&lt;/span&gt;    &lt;span class='hs-keyglyph'&gt;[&lt;/span&gt;&lt;span class='hs-num'&gt;1&lt;/span&gt;&lt;span class='hs-layout'&gt;,&lt;/span&gt; &lt;span class='hs-num'&gt;1&lt;/span&gt;&lt;span class='hs-layout'&gt;,&lt;/span&gt; &lt;span class='hs-num'&gt;0&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;]&lt;/span&gt;&lt;span class='hs-layout'&gt;,&lt;/span&gt;
&lt;span class='hs-varop'&gt;&amp;gt;&lt;/span&gt;    &lt;span class='hs-keyglyph'&gt;[&lt;/span&gt;&lt;span class='hs-num'&gt;0&lt;/span&gt;&lt;span class='hs-layout'&gt;,&lt;/span&gt; &lt;span class='hs-comment'&gt;-&lt;/span&gt;&lt;span class='hs-num'&gt;1&lt;/span&gt;&lt;span class='hs-layout'&gt;,&lt;/span&gt; &lt;span class='hs-num'&gt;1&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;]&lt;/span&gt;&lt;span class='hs-layout'&gt;,&lt;/span&gt;
&lt;span class='hs-varop'&gt;&amp;gt;&lt;/span&gt;    &lt;span class='hs-keyglyph'&gt;[&lt;/span&gt;&lt;span class='hs-num'&gt;2&lt;/span&gt;&lt;span class='hs-layout'&gt;,&lt;/span&gt; &lt;span class='hs-num'&gt;0&lt;/span&gt;&lt;span class='hs-layout'&gt;,&lt;/span&gt; &lt;span class='hs-num'&gt;0&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;]&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;]&lt;/span&gt;
&lt;/pre&gt;
&lt;pre&gt;&lt;span class='hs-varop'&gt;&amp;gt;&lt;/span&gt; &lt;span class='hs-varid'&gt;testAdd&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt; &lt;span class='hs-varid'&gt;testMat1&lt;/span&gt; &lt;span class='hs-varop'&gt;`matAdd`&lt;/span&gt; &lt;span class='hs-varid'&gt;testMat1&lt;/span&gt;
&lt;span class='hs-varop'&gt;&amp;gt;&lt;/span&gt; &lt;span class='hs-varid'&gt;testSub&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt; &lt;span class='hs-varid'&gt;testAdd&lt;/span&gt; &lt;span class='hs-varop'&gt;`matSub`&lt;/span&gt; &lt;span class='hs-varid'&gt;testMat1&lt;/span&gt;
&lt;span class='hs-varop'&gt;&amp;gt;&lt;/span&gt; &lt;span class='hs-varid'&gt;testMult&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt; &lt;span class='hs-varid'&gt;testMat1&lt;/span&gt; &lt;span class='hs-varop'&gt;`matMult`&lt;/span&gt; &lt;span class='hs-varid'&gt;testMat2&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;
    Next time we'll solve linear equations with gaussian elimination
&lt;/p&gt;

&lt;p&gt;
    Updates: Applied CSS colors and made some code changes suggested by Conal
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5463615614172154334-9153798258670359213?l=blog.keithsheppard.name' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.keithsheppard.name/feeds/9153798258670359213/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.keithsheppard.name/2009/06/bird-tracks-through-math-land-basic.html#comment-form' title='12 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5463615614172154334/posts/default/9153798258670359213'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5463615614172154334/posts/default/9153798258670359213'/><link rel='alternate' type='text/html' href='http://blog.keithsheppard.name/2009/06/bird-tracks-through-math-land-basic.html' title='Bird Tracks Through Math Land: Basic Matrix Ops'/><author><name>Keith</name><uri>http://www.blogger.com/profile/04421311170476011799</uri><email>keithshep@gmail.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='09087715816952529339'/></author><thr:total>12</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5463615614172154334.post-8419609512948923138</id><published>2009-05-31T19:17:00.001-07:00</published><updated>2009-05-31T19:51:56.481-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><category scheme='http://www.blogger.com/atom/ns#' term='flat file'/><category scheme='http://www.blogger.com/atom/ns#' term='TxtSushi'/><title type='text'>TxtSushi 0.3.0</title><content type='html'>Just released TxtSushi 0.3.0 which adds:
&lt;ul&gt;&lt;li&gt;Improved SQL parsing and error reporting&lt;/li&gt;&lt;li&gt;GROUP BY ... HAVING support with several aggregate functions (see home page)&lt;/li&gt;&lt;li&gt;A little utility "namecolumns" which will add sequential column names to a table which is useful if your flat file doesn't have a header with column names.
&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5463615614172154334-8419609512948923138?l=blog.keithsheppard.name' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.keithsheppard.name/feeds/8419609512948923138/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.keithsheppard.name/2009/05/txtsushi-030.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5463615614172154334/posts/default/8419609512948923138'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5463615614172154334/posts/default/8419609512948923138'/><link rel='alternate' type='text/html' href='http://blog.keithsheppard.name/2009/05/txtsushi-030.html' title='TxtSushi 0.3.0'/><author><name>Keith</name><uri>http://www.blogger.com/profile/04421311170476011799</uri><email>keithshep@gmail.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='09087715816952529339'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5463615614172154334.post-1054228570598147091</id><published>2009-05-24T06:04:00.000-07:00</published><updated>2009-05-24T06:58:11.606-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><category scheme='http://www.blogger.com/atom/ns#' term='music'/><title type='text'>haskell &amp; music</title><content type='html'>This guy created a pretty cool command line interface to make music with text &lt;a href="http://yaxu.org/haskell-hack"&gt;http://yaxu.org/haskell-hack&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5463615614172154334-1054228570598147091?l=blog.keithsheppard.name' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.keithsheppard.name/feeds/1054228570598147091/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.keithsheppard.name/2009/05/haskell-music.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5463615614172154334/posts/default/1054228570598147091'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5463615614172154334/posts/default/1054228570598147091'/><link rel='alternate' type='text/html' href='http://blog.keithsheppard.name/2009/05/haskell-music.html' title='haskell &amp; music'/><author><name>Keith</name><uri>http://www.blogger.com/profile/04421311170476011799</uri><email>keithshep@gmail.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='09087715816952529339'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5463615614172154334.post-6705281947940500198</id><published>2009-05-21T20:20:00.000-07:00</published><updated>2009-05-21T20:44:23.717-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><category scheme='http://www.blogger.com/atom/ns#' term='flat file'/><category scheme='http://www.blogger.com/atom/ns#' term='TxtSushi'/><title type='text'>TxtSushi 0.2</title><content type='html'>I just released &lt;a href="http://keithsheppard.name/txt-sushi"&gt;TxtSushi&lt;/a&gt; 0.2 with the following updates:
&lt;ol&gt;&lt;li&gt;Improved type coercion. Some of the rules I was using before did not make sense. At some point I will write down what the rules are.&lt;/li&gt;&lt;li&gt;Added some extra functions/operators including a regex matcher. Here is the full list: SUBSTRING, UPPER, LOWER, TRIM, *, /, +, - (binary and unary), =,                     &lt;&gt; (not equal test), &lt;, &lt;=, &gt;, &gt;=, AND, OR, || (string concatination),                     =~ (regex matching)                 &lt;/li&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5463615614172154334-6705281947940500198?l=blog.keithsheppard.name' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.keithsheppard.name/feeds/6705281947940500198/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.keithsheppard.name/2009/05/txtsushi-02.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5463615614172154334/posts/default/6705281947940500198'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5463615614172154334/posts/default/6705281947940500198'/><link rel='alternate' type='text/html' href='http://blog.keithsheppard.name/2009/05/txtsushi-02.html' title='TxtSushi 0.2'/><author><name>Keith</name><uri>http://www.blogger.com/profile/04421311170476011799</uri><email>keithshep@gmail.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='09087715816952529339'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5463615614172154334.post-2687613405440548447</id><published>2009-05-16T19:08:00.000-07:00</published><updated>2009-05-16T19:38:40.549-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><category scheme='http://www.blogger.com/atom/ns#' term='flat file'/><category scheme='http://www.blogger.com/atom/ns#' term='bioinformatics'/><category scheme='http://www.blogger.com/atom/ns#' term='spreadsheet'/><category scheme='http://www.blogger.com/atom/ns#' term='TxtSushi'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><title type='text'>Yay! TxtSushi 0.1</title><content type='html'>I just released my first haskell application called &lt;a href="http://keithsheppard.name/txt-sushi"&gt;TxtSushi&lt;/a&gt;. It's basically useful for processing comma-delimited tables with SQL select statements, plus some other small conversion and formatting utilities. Here's an example that I just tried out with real data and ... it works!
&lt;pre&gt;wget -q -O - ftp://ftp.informatics.jax.org/pub/reports/MRK_List2.rpt | tabtocsv - \
| tssql -table mgi - \
'select `MGI Accession ID`, Symbol, Chr, trim(`cM Position`)
from mgi where (Chr = 1 or Chr = 8 or Chr = 19) and trim(`cM Position`) = "N/A"
order by Chr+0, Symbol' \
| csvtopretty -&lt;/pre&gt;
Which gives you:
&lt;pre&gt;MGI Accession ID|Symbol                     |Chr|TRIM(cM Position)
MGI:3829209     |100039643                  |1  |N/A
MGI:3823247     |100042382                  |1  |N/A
MGI:3826364     |665246                     |1  |N/A
MGI:3828086     |667118                     |1  |N/A
MGI:3829949     |Bmd5a                      |1  |N/A
MGI:3829954     |Bmd5b                      |1  |N/A
MGI:3829959     |Bmd5c                      |1  |N/A
MGI:3762525     |Drinkcacl24                |1  |N/A
MGI:3762526     |Drinkmgcl21                |1  |N/A
MGI:3762535     |Drinkmgcl24                |1  |N/A
MGI:3762388     |Drinksac5                  |1  |N/A
MGI:3836959     |Mir1927                    |1  |N/A
MGI:3836960     |Mir1928                    |1  |N/A
MGI:3837225     |Mir1981                    |1  |N/A
MGI:98018       |OTTMUSG00000002279         |1  |N/A
MGI:3840135     |OTTMUSG00000020948         |1  |N/A
MGI:3834078     |OTTMUSG00000026591         |1  |N/A
MGI:3826770     |Qrr1                       |1  |N/A
MGI:3826773     |Qrr1d                      |1  |N/A
MGI:3826772     |Qrr1p                      |1  |N/A
MGI:3844119     |Sfp1                       |1  |N/A
MGI:3832320     |T(1E2.1;8B1.2)2Lub         |1  |N/A
MGI:3843694     |Tg(tetO-Chrnb2*V287L)H3Gica|1  |N/A
MGI:3720916     |Tgq9                       |1  |N/A
MGI:3640786     |lrm1                       |1  |N/A
MGI:3822907     |384645                     |8  |N/A
MGI:1924337     |Ankrd11                    |8  |N/A
MGI:3844136     |Arrh1                      |8  |N/A
MGI:3705791     |Defa-ps3                   |8  |N/A
MGI:3837211     |Mir1966                    |8  |N/A
MGI:3837213     |Mir1967                    |8  |N/A
MGI:3837215     |Mir1968                    |8  |N/A
MGI:3837216     |Mir1969                    |8  |N/A
MGI:3833469     |OTTMUSG00000016477         |8  |N/A
MGI:3833836     |OTTMUSG00000031120         |8  |N/A
MGI:3844123     |Sfp3                       |8  |N/A
MGI:3628904     |T(Tp(1E2.1);8B1.2)2Lub     |8  |N/A
MGI:3720925     |Tgq18                      |8  |N/A
MGI:3640782     |gpg6                       |8  |N/A
MGI:88396       |Chrm1                      |19 |N/A
MGI:3762554     |Drinkqhcl2                 |19 |N/A
MGI:3762516     |Drinksac2                  |19 |N/A
MGI:3720096     |Hdlq59                     |19 |N/A
MGI:3837023     |Mir1950                    |19 |N/A
MGI:1914960     |Polr2g                     |19 |N/A
MGI:3843453     |Prdt5                      |19 |N/A
MGI:3828068     |Tgq29                      |19 |N/A&lt;/pre&gt;
I created this because it is something that will be useful to my work (flat files are just about everywhere you turn in bioinformatics), but I'm really hoping that this will be something that is generally useful to other people.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5463615614172154334-2687613405440548447?l=blog.keithsheppard.name' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.keithsheppard.name/feeds/2687613405440548447/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.keithsheppard.name/2009/05/yay-txtsushi-01.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5463615614172154334/posts/default/2687613405440548447'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5463615614172154334/posts/default/2687613405440548447'/><link rel='alternate' type='text/html' href='http://blog.keithsheppard.name/2009/05/yay-txtsushi-01.html' title='Yay! TxtSushi 0.1'/><author><name>Keith</name><uri>http://www.blogger.com/profile/04421311170476011799</uri><email>keithshep@gmail.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='09087715816952529339'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5463615614172154334.post-4584004525022956990</id><published>2009-05-06T18:58:00.000-07:00</published><updated>2009-05-06T19:30:53.403-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='maine'/><category scheme='http://www.blogger.com/atom/ns#' term='gay marriage'/><title type='text'>Happy to be in Maine</title><content type='html'>I moved to Maine over a year ago and it is a great place to live for a lot of reasons, but today it's an even better place to live. The governor just signed a bill that makes gay marriage legal! It is also really encouraging to know that this change is taking place within the elected branches of government which shows that we are seeing a popular shift.

It looks like opponents to gay marriage are trying to organize a "peoples veto" so we have to keep pushing forward whether that just means talking to friends and family to explain how you feel or possibly volunteering time or money to organizations like &lt;a href="http://equalitymaine.org/"&gt;EqualityMaine&lt;/a&gt;. Also, those of us lucky enough to see things changing in our own states need to keep doing what we can to change things for gay and lesbian families in the rest of the country.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5463615614172154334-4584004525022956990?l=blog.keithsheppard.name' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.keithsheppard.name/feeds/4584004525022956990/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.keithsheppard.name/2009/05/happy-to-be-in-maine.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5463615614172154334/posts/default/4584004525022956990'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5463615614172154334/posts/default/4584004525022956990'/><link rel='alternate' type='text/html' href='http://blog.keithsheppard.name/2009/05/happy-to-be-in-maine.html' title='Happy to be in Maine'/><author><name>Keith</name><uri>http://www.blogger.com/profile/04421311170476011799</uri><email>keithshep@gmail.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='09087715816952529339'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5463615614172154334.post-8625744258530793402</id><published>2009-05-02T21:15:00.001-07:00</published><updated>2009-05-03T08:50:29.163-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='monads'/><category scheme='http://www.blogger.com/atom/ns#' term='functional programming'/><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><title type='text'>Learning about monads: some suggestions</title><content type='html'>So, I'm relatively new to &lt;a href="http://www.haskell.org/"&gt;Haskell&lt;/a&gt;, and I have to say that &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_0"&gt;monads&lt;/span&gt; were a part of the language that took me a while to get comfortable with. This post is not a tutorial... there are already &lt;a href="http://haskell.org/haskellwiki/Monad_tutorials_timeline"&gt;many &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_1"&gt;monad&lt;/span&gt; tutorials&lt;/a&gt; out on the web that do a better job than I would. Instead this post contains a few of my "lessons learned" about how to best use your time when you are learning &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_2"&gt;monads&lt;/span&gt;.
&lt;ol&gt;&lt;li&gt;My biggest suggestion is that you understand that there is nothing "special" about &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_3"&gt;monad&lt;/span&gt; types. In my initial attempts to learn &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_4"&gt;monads&lt;/span&gt; a lot of the material I read left me with the impression that &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_5"&gt;monads&lt;/span&gt; somehow play by different rules than the rest of the &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_6"&gt;Haskell&lt;/span&gt; language. The &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_7"&gt;monad&lt;/span&gt; class is a plain old &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_8"&gt;Haskell&lt;/span&gt; class that just happens to define an interface that is very useful for implementing a &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_9"&gt;particular&lt;/span&gt; &lt;a href="http://en.wikipedia.org/wiki/Design_pattern_%28computer_science%29"&gt;design pattern&lt;/a&gt; that shows up often with things like IO and error propagation.
&lt;/li&gt;&lt;li&gt;Don't expect to fully understand &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_10"&gt;monads&lt;/span&gt; until you feel comfortable with higher-order functions (functions that take functions as arguments) and &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_11"&gt;Haskell'&lt;/span&gt;s type system (classes). The &lt;a href="http://learnyouahaskell.com/"&gt;Learn You a Haskell&lt;/a&gt; tutorial is a lot of fun and provides a good introduction to these prerequisites.&lt;/li&gt;&lt;li&gt;The "do" notation is useful for formatting reasons but it can also help to reinforce the idea that &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_12"&gt;monads&lt;/span&gt; are somehow special. I recomend that you start learning how to use the &gt;&gt; and &gt;&gt;= functions for &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_0"&gt;monads&lt;/span&gt; sooner rather than later so that you will see how &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_1"&gt;monads&lt;/span&gt; work in a way that is consistent with the rest of Haskell.
&lt;/li&gt;&lt;li&gt;I also think its &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_2"&gt;useful&lt;/span&gt; to read some opinions that are critical of &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_3"&gt;monad&lt;/span&gt; tutorials so that you can make better decisions about how to spend your time. See: &lt;a href="http://byorgey.wordpress.com/2009/01/12/abstraction-intuition-and-the-monad-tutorial-fallacy/"&gt;&lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_4"&gt;Brent's&lt;/span&gt; post on &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_5"&gt;monad&lt;/span&gt; tutorials&lt;/a&gt; and a blog post on &lt;a href="http://ahamsandwich.wordpress.com/2007/07/26/monads-and-why-monad-tutorials-are-all-awful/"&gt;why &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_6"&gt;monad&lt;/span&gt; tutorials are awful&lt;/a&gt;.
&lt;/li&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5463615614172154334-8625744258530793402?l=blog.keithsheppard.name' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.keithsheppard.name/feeds/8625744258530793402/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.keithsheppard.name/2009/05/learning-about-monads-beginners-lessons.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5463615614172154334/posts/default/8625744258530793402'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5463615614172154334/posts/default/8625744258530793402'/><link rel='alternate' type='text/html' href='http://blog.keithsheppard.name/2009/05/learning-about-monads-beginners-lessons.html' title='Learning about monads: some suggestions'/><author><name>Keith</name><uri>http://www.blogger.com/profile/04421311170476011799</uri><email>keithshep@gmail.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='09087715816952529339'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5463615614172154334.post-2425056848245733114</id><published>2009-04-28T20:18:00.000-07:00</published><updated>2009-04-28T20:20:04.093-07:00</updated><title type='text'>Hello World</title><content type='html'>I'll be blogging on life and software. See ya!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5463615614172154334-2425056848245733114?l=blog.keithsheppard.name' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.keithsheppard.name/feeds/2425056848245733114/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.keithsheppard.name/2009/04/hello-world.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5463615614172154334/posts/default/2425056848245733114'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5463615614172154334/posts/default/2425056848245733114'/><link rel='alternate' type='text/html' href='http://blog.keithsheppard.name/2009/04/hello-world.html' title='Hello World'/><author><name>Keith</name><uri>http://www.blogger.com/profile/04421311170476011799</uri><email>keithshep@gmail.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='09087715816952529339'/></author><thr:total>0</thr:total></entry></feed>