<?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:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-5569894</id><updated>2012-01-24T22:16:15.983Z</updated><category term='Unit testing'/><category term='Python'/><category term='Senior moments'/><category term='Vista'/><category term='technology'/><category term='teeth'/><category term='astronomy'/><category term='erlang'/><category term='web'/><category term='gadgets'/><category term='Installers'/><category term='.Net'/><category term='garden'/><category term='France'/><category term='nature'/><category term='Glorantha'/><category term='Film'/><category term='winter'/><category term='AD FS'/><category term='ASP.NET'/><category term='C++'/><category term='WF'/><category term='Scala'/><category term='Notes from the lab'/><category term='accessibility'/><category term='PowerShell'/><category term='software practice'/><category term='Computer Security'/><category term='MSFT utilities'/><category term='Theatre'/><category term='Mileage'/><category term='family'/><category term='Interviews'/><category term='cycling'/><category term='IronPython'/><category term='worldcon'/><category term='usability'/><category term='Federation'/><category term='hardware'/><category term='Identity Management'/><category term='C++/CLR'/><category term='weather'/><category term='Seasonally affective'/><category term='walking'/><category term='silverlight'/><category term='Javascript'/><category term='don&apos;t talk to me about life'/><category term='AJAX/JSON'/><category term='tourism'/><category term='music'/><category term='cats'/><category term='F#'/><category term='traffic hazards'/><category term='Java'/><category term='book'/><category term='concurrency'/><category term='quiz'/><category term='lol people'/><category term='test automation'/><category term='C#'/><category term='Infocard'/><category term='dynamic languages'/><category term='build'/><category term='WCF'/><category term='Ruby'/><category term='food'/><category term='software'/><category term='rpg'/><category term='&apos;C&apos;'/><category term='functional programming'/><category term='OOP'/><category term='house'/><category term='design'/><category term='standards'/><category term='anime'/><category term='mono'/><category term='jython'/><category term='snow'/><category term='WPF'/><category term='test tools'/><category term='web design'/><title type='text'>Distributed Memory</title><subtitle type='html'>Diary, commentary, reviews, snippets to preserve on-line</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default?start-index=101&amp;max-results=100'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>1364</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-5569894.post-7159483041435033893</id><published>2012-01-23T21:45:00.000Z</published><updated>2012-01-24T22:16:15.997Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='test tools'/><category scheme='http://www.blogger.com/atom/ns#' term='PowerShell'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>HTML reporting for StyleCop</title><content type='html'>&lt;p&gt;Based on the outputs from &lt;a href="http://stevegilham.blogspot.com/2012/01/adding-missing-pieces-standalone.html"&gt;the earlier script&lt;/a&gt;; and using an appearance inspired by the output from the XSL report approach from &lt;a href="http://code.google.com/p/codecampserver/source/browse/trunk/lib/CruiseControl.NET/webdashboard/xsl/StyleCopReport.xsl?r=973"&gt;codecampserver&lt;/a&gt;; but with the source in-lined into the report, and violation messages inserted after the affected lines.  Each file (and the nested violation messages) are hidden by default, and can be expanded by clicking the file header or the source marked as violation (where the hand cursor shows):&lt;/p&gt;&lt;p&gt;N.B. Defined as it is within a PowerShell here-string, the jQuery script needs its $ symbols to be escaped to keep the PowerShell parser happy.&lt;/p&gt;&lt;pre class="brush: c#"&gt;&amp;lt;# 
.SYNOPSIS 
    Stylecop HTML report generator. 
    
.DESCRIPTION 
    Creates *.html from *.StyleCop.xml in the current directory 
    These are reports with pretty reporting
        
.NOTES 
    File Name  : Transform-StyleCopReport.ps1 
    Requires   : PowerShell Version 2.0
#&amp;gt; 
function Insert-Newline([System.Xml.XmlElement] $element) {
    $break = $element.OwnerDocument.CreateSignificantWhitespace("`r`n");
    $element.AppendChild($break) | Out-Null
}

function Make-HTML([string] $file) {
    Write-Host "Processing report $file"
    $htmlfile = $file.Replace(".StyleCop.xml", ".html")
    [xml]$raw = Get-Content $file
    [xml]$report = @"
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
&amp;lt;title&amp;gt;StyleCop Report&amp;lt;/title&amp;gt;
&amp;lt;script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;style&amp;gt;
body {position: absolute; top: 0px; width: 100%;margin:0px;min-height:100%;padding:0}
body {font-family:Verdana, Helvetica, sans-serif;font-size:70%;background:#fff;color:#000;}
h1 { padding: 10px; width: 100%; background-color: #566077; color: #fff; }
h2 { margin:10px; padding: 10px; background-color: #ffc; border: #d7ce28 1px solid;  }
h3 { margin:25px; padding: 10px; background: #b9c9fe; color: #039; border: 1px solid #aabcfe; }
h3 span {float: right;}
div { margin:25px; border: #d7ce28 1px solid; }
div div { border: none; }
pre { font-family:Consolas, Inconsolata, "Lucida Console", "Courier New", monospace;font-size:100%; padding: 0; margin:0}
pre.odd { background: #eee; }
pre.even { background: #ddf; }
span.violation { background: #ff8; }
span.index { border-right : 1px solid black }
&amp;lt;/style&amp;gt;
&amp;lt;script type="text/javascript"&amp;gt;
`$(function(){
  // inner first, then outer
  `$('span.violation')
    .click(function(event) {
      if (this == event.target) {
        `$(this).parent().next().toggle();
      }
    return false;
  })
  .css('cursor', 'pointer')
  .click();  
  
  `$('h3')
    .click(function(event) {
      if (this == event.target) {
        `$(this).next().toggle();
      }
    return false;
  })
  .css('cursor', 'pointer')
  .click();
});
&amp;lt;/script&amp;gt;

&amp;lt;/head&amp;gt;

&amp;lt;body&amp;gt;
&amp;lt;h1&amp;gt;StyleCop Report&amp;lt;/h1&amp;gt;

&amp;lt;/body&amp;gt;

&amp;lt;/html&amp;gt;
"@
    $body = (Select-Xml -Xml $report -XPath "//body").Node
    $doc = $body.OwnerDocument
    $doc.PreserveWhitespace = $true
    Insert-Newline $body
    
    $x = $doc.CreateElement("h2", "")
    $violations = (Select-Xml -Xml $raw -XPath "//Violation")
    
    $x.InnerText = "Total Violations : $($violations.Length)"
    $body.appendChild($x) | Out-Null
    Insert-Newline $body
    
    $vfiles = @($violations | % { $_.Node.Source } | Sort-Object -Unique)
    
    $vfiles | % { 
        $path = Resolve-Path $_
        $local = (Select-Xml -Xml $raw -XPath "//Violation[@Source='$_']")

        $h = $doc.CreateElement("h3", "")
        $body.AppendChild($h) | Out-Null
        Insert-Newline $body
        
        $t = $doc.CreateTextNode("SourceFile: $path")
        $span = $doc.CreateElement("span", "")
        
        $span.InnerText = "($($local.Length) violations)"
        $h.AppendChild($t)     | Out-Null
        $h.AppendChild($span)  | Out-Null
        
        $sdiv = $doc.CreateElement("div", "")
        $body.AppendChild($sdiv) | Out-Null
                    
        $source = Get-Content $path
        
        $line = 0
        $source | % {
            $line += 1
            $pre = $doc.CreateElement("pre", "")
            $num = $line.ToString()
            while ($num.Length -lt 4) { $num = " " + $num }
            $span1 = $doc.CreateElement("span", "")
            $span1.InnerText = "$num "
            $span1.SetAttribute("class", "index")
            $pre.AppendChild($span1) | Out-Null
            
            $span2 = $doc.CreateElement("span", "")
            $span2.InnerText = $_
            $pre.AppendChild($span2) | Out-Null
            
            $sdiv.AppendChild($pre) | Out-Null
            $pre.SetAttribute("class", "odd")
            if (0 -eq ($line % 2)) { $pre.SetAttribute("class", "even") }
            
            $localhere = @($local | % { $_.Node } | ? { $_.LineNumber -eq $line })
            if ($localhere.Length -gt 0) { 
                $span2.SetAttribute("class", "violation")
                $pdiv = $doc.CreateElement("div", "")
                Insert-Newline $sdiv
                $sdiv.AppendChild($pdiv) | Out-Null
                $ul = $doc.CreateElement("ul", "")
                $pdiv.AppendChild($ul) | Out-Null
                $localhere | % {
                    $li = $doc.CreateElement("li", "")
                    $li.InnerText = $_.InnerText
                    $ul.AppendChild($li) | Out-Null
                    Insert-Newline $ul
                }
            }
            
            Insert-Newline $sdiv
        }
    } # $vfiles
    
    $report.Save($htmlfile)
} # function

$files = @(dir *.StyleCop.xml)
$files | % { Make-HTML $_.FullName }&lt;/pre&gt;&lt;p&gt;You will need to adjust the script to find the appropriate StyleCop output files from your build process, and create suitably named/located reports based on those files.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-7159483041435033893?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/7159483041435033893/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=7159483041435033893' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/7159483041435033893'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/7159483041435033893'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2012/01/html-reporting-for-stylecop.html' title='HTML reporting for StyleCop'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-3356491939251683630</id><published>2012-01-22T20:14:00.001Z</published><updated>2012-01-22T20:14:31.507Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='winter'/><category scheme='http://www.blogger.com/atom/ns#' term='weather'/><category scheme='http://www.blogger.com/atom/ns#' term='garden'/><title type='text'>Garden rubbish</title><content type='html'>&lt;p&gt;Apart from a brief spell around last weekend, and into the start of the past week, where temperatures driving to work were in the -3C to -5C range, winter hasn't really arrived -- we've just had an extended autumn.&lt;/p&gt;&lt;p&gt;The cold finally did for the the antirrhinum flowers, and the last signs of life in the sunflowers, which are still serving as bird-feeders; but we still have overwintering escholzias; self-sown poached-egg plants from last summer's flowering already in bloom, primroses since before the end of last year, and now snowdrops and the first of the early crocuses.&lt;/p&gt;&lt;p&gt;I have at least been able to tend to a number of the tidy-up chores : pruning roses, fuchsias and apple trees; clearing out the straw from various dead annuals, and the last year's growth from the crocosmias and lemon balm.&lt;/p&gt;&lt;p&gt;And of course there are weeds already -- including a fine old crop of winter wheat from the porridge-like mass of spilt grain out of the bird feeder, as well as the usual stubborn perennials, misplaced self-seedings and the like.  Which all means that the green bin is continually being put out full, even in the putative low season.&lt;/p&gt;&lt;p&gt;If it were drier, there'd even be grass clippings, as the mild wet weather is encouraging the lawn already!&lt;/p&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-3356491939251683630?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/3356491939251683630/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=3356491939251683630' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/3356491939251683630'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/3356491939251683630'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2012/01/garden-rubbish.html' title='Garden rubbish'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-5778985677188902296</id><published>2012-01-22T19:59:00.000Z</published><updated>2012-01-23T17:33:14.505Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='test tools'/><category scheme='http://www.blogger.com/atom/ns#' term='PowerShell'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Adding the missing pieces to the Standalone StyleCop script</title><content type='html'>&lt;p&gt;Rather than post the whole ~200 lines, just the salient bits so that &lt;a href="http://stevegilham.blogspot.com/2012/01/standalone-stylecop-rough-draft.html"&gt;the original&lt;/a&gt; can be tidied up (but without obscuring everything with obsessive error handling).  Start by giving it parameters like&lt;/p&gt;&lt;pre class="brush: c#"&gt;&amp;lt;# 
.SYNOPSIS 
    Stand-alone stylecop runner. 
    
.DESCRIPTION 
    Runs StyleCop over a project or a solution
        
.NOTES 
    File Name  : Run-StyleCop.ps1 
    Requires   : PowerShell Version 2.0

.PARAMETER FilesToScan

Project or Solution file

.PARAMETER StyleCopFolder

To override the heuristic looking in $env:ProgramFiles for the latest version
e.g. for StyleCop in source control

.PARAMETER OnlyBuildIntegrated

Only scan project files which include &amp;lt;Import Project="something with 'stylecop' in it" /&amp;gt;
#&amp;gt;
param ( 
    [Parameter(Mandatory = $true)] [string] $FilesToScan,
    [string] $StyleCopFolder,
    [switch] $OnlyBuildIntegrated)
&lt;/pre&gt;&lt;p&gt;Find StyleCop in the default install location by&lt;/p&gt;&lt;br /&gt;
&lt;pre class="brush: c#"&gt;if (-not $StyleCopFolder) {
    $styleCopFolder =  (dir "$env:programFiles\stylecop*" | Sort-object LastWriteTimeUtc | Select-Object -Last 1).FullName
}&lt;/pre&gt;&lt;p&gt;Get the list of projects by either just the project, or using a regex on the project file from here: &lt;a href="http://bytes.com/topic/c-sharp/answers/483959-enumerate-projects-solution"&gt;http://bytes.com/topic/c-sharp/answers/483959-enumerate-projects-solution&lt;/a&gt;&lt;/p&gt;&lt;br /&gt;
&lt;pre class="brush: c#"&gt;if ($FilesToScan -like "*.csproj") {
    $projectList = ,$FilesToScan
} else {
    if ($FilesToScan -like "*.sln") {
        $solutionDir = Split-Path $FilesToScan
        $matchProjectNameRegex =
            '^Project\("(?&amp;lt;PROJECTTYPEGUID&amp;gt;.*)"\)\s*=\s* "(?&amp;lt;PROJECTNAME&amp;gt;.*)"\s*,\s*"(?&amp;lt;PROJECTRELATIVEPATH&amp;gt;.*)"\s*,\s*"(?&amp;lt;PROJECTGUID&amp;gt;.*)"$'
        # or we could filter on project guid being {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
        $projectList = @(Get-Content $FilesToScan | % { 
                        $matches = $null; $_ -match $matchProjectNameRegex | Out-Null; $matches.PROJECTRELATIVEPATH } | ? {
                        $_ -like "*.csproj" } | % { Join-Path $solutionDir $_ } )
    } else {
        Write-Error "$FilesToScan isn't a solution file or a C# project"
    }
}

# Check if required for a StyleCop target
if ($OnlyBuildIntegrated) {
    $projectList = @($projectList | ? {
        [xml]$content = Get-Content $_
        $imports = @($content.Project.Import)
        $flagged = (@($imports | % { $_.Project } | ? { $_ -like "*stylecop*" })).Length
        if ($flagged -eq 0) { Write-Host "Skipping project $_" }
        ($flagged -gt 0)
})}&lt;/pre&gt;&lt;p&gt;Find a &lt;code&gt;Settings.StyleCop&lt;/code&gt; file by looking at the project folder and up, defaulting to one in the StyleCop folder; and then scan the C# files of interest by&lt;/p&gt;&lt;br /&gt;
&lt;pre class="brush: c#"&gt;[xml]$content = Get-Content $projectFile
        # All C# files
        $cs = @($content.Project.ItemGroup | % { $_.Compile } | ? {$_})
        
        # Filter excluded files; get absolute paths
        $projectDir = Split-Path $_
        $cs = @($cs | ? { -not (($cs[0].ExcludeFromStyleCop -like "true") -or ($cs[0].ExcludeFromSourceAnalysis -like "true")) 
            } | % { Join-Path $projectDir $_.Include })
        
        $cs | % { $console.Core.Environment.AddSourceCode($codeproject, $_, $null) | Out-Null }&lt;/pre&gt;&lt;br /&gt;
&lt;p&gt;and customise the output file as &lt;/p&gt;&lt;br /&gt;
&lt;pre class="brush: c#"&gt;$name = (Split-Path $projectFile -Leaf).Replace(".csproj", ".StyleCop.xml")
    $outputXml = Join-Path (Get-Location) $name&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-5778985677188902296?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/5778985677188902296/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=5778985677188902296' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/5778985677188902296'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/5778985677188902296'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2012/01/adding-missing-pieces-standalone.html' title='Adding the missing pieces to the Standalone StyleCop script'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-5973223806117883779</id><published>2012-01-21T23:14:00.000Z</published><updated>2012-01-21T23:22:50.468Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='test tools'/><category scheme='http://www.blogger.com/atom/ns#' term='software practice'/><category scheme='http://www.blogger.com/atom/ns#' term='PowerShell'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Standalone StyleCop : a rough draft</title><content type='html'>&lt;p&gt;Unlike FxCop, the StyleCop tool out of the box only comes with Visual Studio and MSBuild integration; there is no stand-alone command-line tool.  Still, as it's now open-source, we can look at the workings of the MSBuild task and see how to drive it directly.&lt;/p&gt;&lt;p&gt;Here is a proof-of-concept PowerShell script which can be used as the basis for a proper command-line tool or script; and as a preliminary "how to" to write unit tests for your rules.  Well, they aren't going to be proper unit tests, since they'd have to touch the file system, but they can crawl over test source files in a dummy project in the StyleCop rule solution.&lt;/p&gt;&lt;pre class="brush: c#"&gt;# Load StyleCop
$path = "$($env:ProgramFiles)\StyleCop 4.5\StyleCop.dll"
Add-Type -Path $path

# arguments that must be non-null, non-empty
$settings = "$($env:UserProfile)\Documents\Visual Studio 2008\Projects\TestBed\Settings.StyleCop"
$here = Get-Location
$outputFile = Join-Path $here "output.xml"

# UNTESTED : the empty array should be filled with a list of directories containing 
# StyleCop add-ins that aren't in or beneath the StyleCop install folder
$console = new-object StyleCop.StyleCopConsole -arg  @($settings, $false, $outputFile, [string[]]@(), $true)

# Usual test case #defines
$defines = [string[]]@("DEBUG", "CODE_ANALYSIS")
$configuration = new-object StyleCop.Configuration -arg (,$defines)
$project = "$($env:UserProfile)\Documents\Visual Studio 2008\Projects\TestBed\TestBed.csproj"
$codeproject = new-object StyleCop.CodeProject -Arg @($project.GetHashCode(), $project, $configuration)
[StyleCop.CodeProject[]] $projects = ,$codeproject

## TODO -- get source files from project, do AddSourceCode for each file not excluded from StyleCop
$source = "$($env:UserProfile)\Documents\Visual Studio 2008\Projects\TestBed\Class1.cs"
$console.Core.Environment.AddSourceCode($codeproject, $source, $null) | Out-Null

# These are messages suitable for MSBuild e.g. "Output: (Low) Pass 1: Class1.cs"
$outputHandler = {
    $e = $event.SourceEventArgs
    $importance = $e.Importance
    $message = $e.Output

    $colour = "Green"
    if ($importance -eq [Microsoft.Build.Framework.MessageImportance]::Low) { $colour = "Gray" }
    if ($importance -eq [Microsoft.Build.Framework.MessageImportance]::High) { $colour = "Red" }
    Write-Host "Output: ($importance) $message" -Back White -Fore $colour
}

# These are the interesting bits, which also go into the XML file e.g.
# Violation SA1200 in ...\Class1.cs (1)
# All using directives must be placed inside of the namespace.
$violationHandler = {
    $e = $event.SourceEventArgs
    if ($e.SourceCode -and $e.SourceCode.Path) {
        $file = $e.SourceCode.Path;
    } else {
 if ($e.Element -and $e.Element.Document -and $e.Element.Document.SourceCode -and $e.Element.Document.SourceCode.Path)
        {
            $file = $e.Element.Document.SourceCode.Path;
        }
    }

    $colour = "Black"
    if ($e.Warning) { $colour = "Blue" }
    Write-Host "Violation $($e.Violation.Rule.CheckId) in $file ($($e.LineNumber))`r`n`t$($e.message)" -Back White -Fore $colour
}

try {
    Register-ObjectEvent -InputObject $console -EventName OutputGenerated -SourceIdentifier "Console.OutputGenerated" -Action $outputHandler | Out-Null
    Register-ObjectEvent -InputObject $console -EventName ViolationEncountered -SourceIdentifier "Console.ViolationEncountered" -Action $violationHandler | Out-Null
    $Console.Start($projects, $true) | Out-Null
} catch [System.Exception] {
    Write-Host $_.Exception.ToString()
} finally {
    Unregister-Event "Console.OutputGenerated"
    Unregister-Event "Console.ViolationEncountered"
}

## Could dump the XML format output here, like this; or run an XSLT report generation over it
##$values = Get-Content $outputFile
##Write-Host $values$outputFile&lt;/pre&gt;&lt;p&gt;And that's all there is to it, really.&lt;/p&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-5973223806117883779?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/5973223806117883779/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=5973223806117883779' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/5973223806117883779'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/5973223806117883779'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2012/01/standalone-stylecop-rough-draft.html' title='Standalone StyleCop : a rough draft'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-7641218786264019406</id><published>2011-12-31T23:51:00.001Z</published><updated>2011-12-31T23:51:23.760Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='cycling'/><title type='text'>3202.5</title><content type='html'>&lt;p&gt;That's the cumulative cycling mileage at the end of the year, passing the 3200 target I wanted to hit (~2170 over the year, plus the 90-odd on a hired bike to make over 2250 total).  That's omitting those miles where I'd forgotten to put the widget back in place to count the distance; but that is unlikely to be more than about 5 miles total, and could be made up with distances pushed anyway.&lt;/p&gt;&lt;p&gt;I think it goes to show how dry the weather was for much of the year.&lt;/p&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-7641218786264019406?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/7641218786264019406/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=7641218786264019406' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/7641218786264019406'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/7641218786264019406'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/12/32025.html' title='3202.5'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-4146216822148884149</id><published>2011-12-31T21:48:00.000Z</published><updated>2011-12-31T23:46:02.141Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='anime'/><title type='text'>Anime -- 2011 in review (part 1)</title><content type='html'>&lt;p&gt;In total, I have watched all through just 3 series from the last year, am in the middle of two that have completed, one that hasn't and am about to start on one that has.  Plus a scattering of older shows finished or in train.  This post, old shows considered.&lt;/p&gt;&lt;br /&gt;
&lt;h4&gt;Mobile Suit Gundam&lt;/h4&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-_Ee7bet9tVs/Tv91Z9qjT6I/AAAAAAAABEU/5ApQDoVmDa4/s1600/gundam0079.jpg" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="240" width="320" src="http://4.bp.blogspot.com/-_Ee7bet9tVs/Tv91Z9qjT6I/AAAAAAAABEU/5ApQDoVmDa4/s320/gundam0079.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;p&gt;I remember when the future looked like Gundam - O'Neill colonies throughout the Earth-Moon system, but no internet.  Now add brutal robot combat, the usual Tomino level of named character futile deaths, mixed with toy commercials.  Like the following year's &lt;i&gt;Space Runaway Ideon&lt;/i&gt;, this has an element of a treadmill, where each episode the enemy -- here the L2-based Principality of Zeon -- uprates its mobile suits each episode and our guys with their carrier and mobile suits continue to level up to meet them.&lt;/p&gt;&lt;p&gt;After ten episodes of this, the action moves ground-side, with a long meander from south-east Asia, through to the Black Sea (with a number of side trips -- "we need to reprovision with salt"; our hero has an angst attack and has wandered off with the Gundam; ...) thence to Ireland  (where we meet a traditional red-headed Irish girl, with the traditional Irish name of Miharu), then across the Atlantic to a Federation base in South America, having circumnavigated the globe rather than going direct.&lt;/p&gt;&lt;p&gt;Then, finally, we return to space for a series of skirmishes, culminating in an escalating final battle where pretty much everything gets destroyed; but the power of Zeon is broken and all seems well (until the next series).&lt;/p&gt;&lt;p&gt;Even after 30 years, and with an indifferent American dub, the series holds up as more than just a piece of history.  The anti-war message may be unsubtle, but there is a feeling of how even the significant players like the Gundam team are just a part of a larger concern; and the daring and charismatic recurring antagonist, Char Aznable is in turn just a minor, though equally significant, character on the Zeon side.&lt;/p&gt;&lt;br /&gt;
&lt;h4&gt;Catman&lt;/h4&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-MloP9atH-30/Tv99_UXNJkI/AAAAAAAABEk/lWxxs0HMezI/s1600/Catman.jpg" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="180" width="320" src="http://4.bp.blogspot.com/-MloP9atH-30/Tv99_UXNJkI/AAAAAAAABEk/lWxxs0HMezI/s320/Catman.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;p&gt;A quirky little flash series about a down-and-out in an alternate Canada peopled by anthropomorphic cats.  It's slice of life anime done right. That means without any cute girls, school, or even cake, but with a whole lot of drinking, swearing, bowling, getting beaten up, and freezing to death on the street. And catchy ska BGM from the Planet Smashers.&lt;/p&gt;&lt;br /&gt;
&lt;h4&gt;Hyakko&lt;/h4&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-sZ8JWEuF7VY/Tv9-27HROEI/AAAAAAAABE0/_MDwUZLFlhA/s1600/hyakko2.jpg" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="178" width="320" src="http://2.bp.blogspot.com/-sZ8JWEuF7VY/Tv9-27HROEI/AAAAAAAABE0/_MDwUZLFlhA/s320/hyakko2.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;p&gt;A series I passed by back in the busy year of 2008, as another cute girls at school series like &lt;i&gt;Hidamari Sketch&lt;/i&gt; or &lt;i&gt;Sketchbook Full Colours&lt;/i&gt;; but which turned out to have rather more energy and enthusiasm about what it's doing.  We start by having the ditzy bonde Ayumi as the first viewpoint character lost in her vast new school, then accumulate the oujo-sama Tatsuki (also lost), and the core group is then completed by the sudden eruption from an upper storey window of the tomboyish Torako, followed by her constant companion, Suzume (who are solving the navigation problem by heading in a straight line).&lt;/p&gt;&lt;p&gt;The series takes a little time finding its feet, and goes thematically all over the place, from out and out comedy, to amped-up melodrama; but as we learn about the quartet and the rest of the class of equally idiosyncratic types, everyone is refreshingly nice (except for the boys in the senior years, who are jerks, and Torako's family, who exist mainly for injecting drama).&lt;/p&gt;&lt;p&gt;Not an undying classic of the ages, but still a nice feel-good series to have watched over the holidays; especially as it was set in early summer, sunny and bright, a perfect antidote to the midwinter blues; the one exception, the April rainstorm that opens the final flashback episode, where the melancholy dawn of the first day of the school year breaks.  That scene was the one where the incidental music was most noticeable, and it seemed to fit the bitter-sweet flavour of the whole episode.&lt;/p&gt;&lt;br /&gt;
&lt;h4&gt;Natsume Yuujinchou -- seasons 1 and 2&lt;/h4&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-cWKWRMUjtGM/Tv-DoVedc6I/AAAAAAAABFE/BXz_2fGqBRU/s1600/natsume_0001.jpg" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="180" width="320" src="http://4.bp.blogspot.com/-cWKWRMUjtGM/Tv-DoVedc6I/AAAAAAAABFE/BXz_2fGqBRU/s320/natsume_0001.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;p&gt;Another series from 2008 (and then 2009); somewhat like &lt;i&gt;&lt;a href="http://stevegilham.blogspot.com/2007/03/anime-mushishi.html"&gt;Mushishi&lt;/a&gt;&lt;/i&gt;, in that Natsume Takashi has the ability to see spirits that pass by normal people unobserved.  However, unlike the competent adult Ginko, who has to deal with fungal or insectile &lt;i&gt;mushi&lt;/i&gt;, the &lt;i&gt;youkai&lt;/i&gt; that highschooler Natsume can see are people of a sort.&lt;/p&gt;&lt;p&gt;This unsettling aspect of his life, reactions to and conversations with the unseen, has left him shuffled from relative to relative.  This time he also stumbles across some of his late grandmother's possessions -- including the eponymous book in which she inscribed the true names of many &lt;i&gt;youkai&lt;/i&gt;; and into a shrine where a powerful spirit is bound who Natsume accidentally frees.&lt;/p&gt;&lt;p&gt;Having spent many years bound in the form of a lucky cat, the spirit now appears as a human-visible cat most of the time; and sets himself up as Natsume's bodyguard : no-one else is getting that book but him.&lt;/p&gt;&lt;p&gt;So with the rascally Nyanko-sensei to guide and protect him, he starts to find and unbind some of the &lt;i&gt;youkai&lt;/i&gt; in the Book of Friends.  The series thus starts as a simple monster of the week; but as the extended cast is gathered, it becomes as much about resolving problems amongst the &lt;i&gt;youkai&lt;/i&gt;, and Natsume worrying about how his life is balanced between the two worlds.&lt;/p&gt;&lt;p&gt;While being much the typical anime protagonist most of the time, the show avoids getting Natsume into any of the usual romance clichés, without actively excluding girls from his social circle (such as it is).  The fact that many of them are spirits, and one explicitly declares that she was much more interested in his grandmother, helps avoid that sort of thing.&lt;/p&gt;&lt;p&gt;So a quiet and delightful little series; and in the new year, I shall move onto the recently aired 3rd season, and the about to air 4th.&lt;/p&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-4146216822148884149?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/4146216822148884149/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=4146216822148884149' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/4146216822148884149'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/4146216822148884149'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/12/anime-2011-in-review-part-1.html' title='Anime -- 2011 in review (part 1)'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-_Ee7bet9tVs/Tv91Z9qjT6I/AAAAAAAABEU/5ApQDoVmDa4/s72-c/gundam0079.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-3043480049286789114</id><published>2011-12-28T19:55:00.000Z</published><updated>2011-12-31T19:59:16.293Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Film'/><title type='text'>Film -- The Adventures Of Tintin: Secret Of The Unicorn</title><content type='html'>&lt;p&gt;Last cinema visit of the year, catching the holiday re-run of the film.&lt;/p&gt;&lt;p&gt;Not having read any of the source material in decades, I didn't worry about anything that might offend purists, but enjoyed the cartoony inter-war era high adventure in the style of what I did recall.&lt;/p&gt;&lt;p&gt;We will know that the CGI medium, especially in its 3D expression, is mature when they stop showing off gratuitous mirrors and lenses.&lt;/p&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-3043480049286789114?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/3043480049286789114/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=3043480049286789114' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/3043480049286789114'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/3043480049286789114'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/12/film-adventures-of-tintin-secret-of.html' title='Film -- &lt;i&gt;The Adventures Of Tintin: Secret Of The Unicorn&lt;/i&gt;'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-8635235687117858266</id><published>2011-12-27T01:24:00.000Z</published><updated>2011-12-27T10:01:57.638Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='build'/><category scheme='http://www.blogger.com/atom/ns#' term='MSFT utilities'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Writing StyleCop rules in F#</title><content type='html'>&lt;p&gt;Alas not "Writing StyleCop rules &lt;i&gt;for&lt;/i&gt; F#" (or should that be &lt;i&gt;Style&lt;b&gt;F&lt;/b&gt;op&lt;/i&gt;?), which would be nice, but a true job of work...&lt;/p&gt;&lt;p&gt;I don't see much need for more StyleCop rules for C# (except one to harness the FxCop spellcheck facilities to scan comments, which would have to be done via much dodgy reflection to winkle out internal types, and would anyway give issues for being tied to 32-bit native executables), so haven't tried this before.  But, like mountains, it's there...&lt;/p&gt;&lt;hr /&gt;&lt;p&gt;So, you have StyleCop 4.5 or 4.6 installed, and start by creating an empty class library targeted at the .net 3.5 environment, with added references to the StyleCop and StyleCop.CSharp assemblies.  The bare rule class looks like&lt;/p&gt;&lt;pre class="brush: f#"&gt;open StyleCop
open StyleCop.CSharp

namespace My.Namespace

[&amp;lt;SourceAnalyzer(typeof&amp;lt;CsParser&amp;gt;)&amp;gt;]
type MyRules() =
    inherit SourceAnalyzer()&lt;/pre&gt;&lt;p&gt;and to go with it, add an XML file as an Embedded Resource with name &lt;code&gt;My.Namespace.MyRules.xml&lt;/code&gt; -- &lt;b&gt;first gotcha&lt;/b&gt; : the long name needs to be given in full because the default namespace doesn't get applied in the build like it would for C#.  That can then be filled in as per the instructions in the &lt;a href="http://stylecop.codeplex.com/releases/view/62209"&gt;StyleCop SDK documentation&lt;/a&gt;. Then just override the &lt;code&gt;AnalyzeDocument&lt;/code&gt; method and go wild:&lt;/p&gt;&lt;pre class="brush: f#"&gt;override self.AnalyzeDocument (document : CodeDocument) =
        let csharp = document :?&amp;gt; CsDocument
        self.CheckForTrailingSpaces csharp
        // add more rules here...&lt;/pre&gt;&lt;p&gt;where I'm using the concept from the &lt;a href="http://stylecopplus.codeplex.com/"&gt;StyleCop+&lt;/a&gt; rule SP2000 as an example of a new rule not yet present in the core tool.&lt;/p&gt;&lt;p&gt;Unlike FxCop, there is not a "one class = one rule" model here; the classes exist to group related rules, and you control how each scans the current source file of interest, and, indeed, how distinct they are in the code -- a closely related set of rules could be checked off a single scan, and in some ways StyleCop rules are more akin to the distinct named resolutions that can be defined within a single FxCop rule.&lt;/p&gt;&lt;p&gt;Another consequent difference from FxCop is how the object graph is visited.  In FxCop, there are VisitXxx methods to override that will be called for every object of type Xxx, in an essentially stateless manner (your rule class is responsible for maintaining state between calls); here the equivalent methods are passed to a WalkXxx graph walker method as a callback, provide a mutable state object argument, and can signal through their return value whether the traversal should continue.&lt;/p&gt;&lt;p&gt;This approach of using a mutable object, needing down-casting, to carry inputs into each call, and contain the resultant outputs, feels a bit odd in a functional environment; fortunately it is possible to bypass this in many cases by representing the tree-walk as a seq and operating on that.  For the trailing whitespace rule, where the offending lines are most easily found by scanning the raw source line by line, all we need the traversal for is to find an &lt;code&gt;ICodeElement&lt;/code&gt; context object matching that line number.  A simple walk of the code element tree looks like&lt;/p&gt;&lt;pre class="brush: f#"&gt;let rec descent (x:CsElement) = seq {
                                                yield x
                                                if x.ChildElements &amp;lt;&amp;gt; null then
                                                    for c in x.ChildElements do
                                                        yield! descent c
                                            }&lt;/pre&gt;&lt;p&gt;which we apply to the document &lt;code&gt;RootElement&lt;/code&gt; and from the result find the last (so most deeply nested, and hence most constrained) item whose &lt;code&gt;Location&lt;/code&gt; has a span that includes the line number of interest (skip while the end is before the line of interest, take while the start includes the line, reverse, get head), the line number being a value that the operation can close over.&lt;/p&gt;&lt;p&gt;For deployment onto machines that don't have F# installed, static linkage of the runtime via the --standalone build flag can be used rather than having to explicitly drop the F# runtime assembly into the StyleCop folder (&lt;b&gt;second gotcha&lt;/b&gt;).&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-8635235687117858266?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/8635235687117858266/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=8635235687117858266' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/8635235687117858266'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/8635235687117858266'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/12/writing-stylecop-rules-in-f.html' title='Writing StyleCop rules in F#'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-1946548292829733969</id><published>2011-12-26T23:58:00.000Z</published><updated>2011-12-26T23:58:06.076Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='weather'/><category scheme='http://www.blogger.com/atom/ns#' term='cycling'/><category scheme='http://www.blogger.com/atom/ns#' term='garden'/><title type='text'>A mild Christmas, a welcome change</title><content type='html'>&lt;p&gt;Friday, with the temperature about 5C, and strong sou'westerly blowing, I cycled to work, thinking all the while that I was mad for doing so, with a weather forecast for rain, mainly heavy, in the afternoon.  Unlike last year, there wasn't the need to wear anything over my spandex shorts for warmth, so that would be enough to mitigate the worst of cycling in the rain.  As it turned out, when I set off home a while after 2, it was starting to sprinkle with rain; eventually getting heavy enough to want a waterproof, but not really uncomfortable at all.&lt;/p&gt;&lt;p&gt;Saturday, a muntjac wandered into the garden, and ate some of the remaining windfall apples, the ones too small or damaged to have harvested.  The cats were not amused by this &lt;i&gt;thing&lt;/i&gt; invading their territory, and kept a wary eye on it from a distance, and well away as it wandered off again.  At least it was too big to fit through the cat-flap, unlike pigeons or the various rodents whose entrails at times have decorated the kitchen floor; more like the pheasant that was left on the doorstep, with just a clear cat-inflicted bare patch on its neck a couple of weeks ago.&lt;/p&gt;&lt;p&gt;Yesterday began with the exchange of presents -- where I got Vinge's latest, novel, &lt;a href="http://www.amazon.com/Children-Sky-Zones-Thought/dp/0312875622/ref=sr_1_1"&gt;&lt;i&gt;The Children of the Sky&lt;/i&gt;&lt;/a&gt;, the long awaited sequel to 1993 Hugo winner, &lt;i&gt;A Fire Upon the Deep&lt;/i&gt;; and &lt;a href="http://www.timworstall.com"&gt;Tim Worstall&lt;/a&gt;'s book of essays on economy and the environment &lt;a href="http://www.amazon.com/Chasing-Rainbows-Economic-Myths-Environmental/dp/1906768447/ref=sr_1_1"&gt;&lt;i&gt;Chasing Rainbows&lt;/i&gt;&lt;/a&gt;, which metaphorically in text is like &lt;i&gt;suikawari&lt;/i&gt; without the blindfold; and then lunch.&lt;/p&gt;&lt;p&gt;Having not thought to book until 5 weeks ago, we had a simple enough choice of places where there were still slots to be had -- lunch at the Carpenters Arms.  &lt;br /&gt;
&lt;/p&gt;This was a leisurely affair, 12:30 for 1, canapés, starters, then buffet style mains, where I was unable to take up the offer of a third helping, before the cheese board, a little Xmas pudding, and coffee with petites-fours; and so home in gentle fashion by not too long after 5pm.&lt;p&gt;So today could be pure R&amp;amp;R, with just a little bit of pottering in the garden for some fresh air -- clearing the tomato plants out of the greenhouse, hoovering the lawn (to get rid of fallen leaves), trimming where it had straggled over the borders, and clearing the few places where plants had stopped for the winter and obligingly dried up (the marigolds are still in flower, as are the antirrhinums; not just the viburnum which I'd expect to be flowering now); leaving a little space in the green bin for more gardening later in the week.&lt;/p&gt;&lt;p&gt;Some of the sunflower heads that haven't already been stripped of seeds, I hung up for the birds; and the last handful of rosebuds are in a vase by the fireplace.  And so the garden remains in a late-autumn limbo, until I really can't postpone pruning the roses and apple trees any more.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-1946548292829733969?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/1946548292829733969/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=1946548292829733969' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/1946548292829733969'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/1946548292829733969'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/12/mild-christmas-welcome-change.html' title='A mild Christmas, a welcome change'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-4009470264898375864</id><published>2011-12-14T22:22:00.001Z</published><updated>2011-12-14T22:22:06.107Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><title type='text'>An approximate type-based solution to the strings problem</title><content type='html'>&lt;p&gt;As ever, neat ideas coded in Haskell can be almost but not quite ported to F#, foiled at the last moment by the fact that the .net type system cannot represent constructs that are expressed as Haskell type classes : there's always a run-time type coercion and some leakage of abstractions that have to be coded around.&lt;/p&gt;&lt;p&gt;One such is this idea of &lt;a href="http://tea.moertel.com/articles/2006/10/18/a-type-based-solution-to-the-strings-problem"&gt;type-safe strings for representing structured data like XML&lt;/a&gt; -- which would require a little more mutually recursive and self-encapsulating type definitions than I think F# can manage.  It gets close, though, and could probably be embellished a little further:&lt;br /&gt;
&lt;/p&gt;&lt;pre class="brush: f#"&gt;#light

open System

module TypeStrings =

    let rec foldr f z l =
        match l with
        | [] -&amp;gt; z
        | x::xs -&amp;gt; f x (foldr f z xs)

    [&amp;lt;AbstractClass&amp;gt;]
    type SafeStringBase(content : String) =
        member this.AsString with get() = content
        override this.ToString() = content
        
    // Now here #SafeStringBase really means SafeString of the corresponding 
    // concrete language type but there isn't a way to express that
    type ILanguage =
        abstract member LiteralFragment         : String -&amp;gt; #SafeStringBase // String is a literal language fragment
        abstract member LiteralText             : String -&amp;gt; #SafeStringBase // String is literal text
        abstract member NativeRepresentation    : #SafeStringBase -&amp;gt; String   // Gets the native-language representation
        abstract member Language                : unit -&amp;gt; String             // Gets the name of the language  
        abstract member Empty                   : unit -&amp;gt; #SafeStringBase     // creates an empty SafeString in the Language
        abstract member Add                     : #SafeStringBase -&amp;gt; #SafeStringBase -&amp;gt; #SafeStringBase
        
    type SafeString&amp;lt;'TLanguage when 'TLanguage :&amp;gt; ILanguage and 'TLanguage : (new : unit -&amp;gt; 'TLanguage) &amp;gt; (content : String) =
        inherit SafeStringBase(content)
        static let language : 'TLanguage = new 'TLanguage()
        
        // this is ugly
        static member private ToType (x:#SafeStringBase) =
            x :&amp;gt; SafeStringBase :?&amp;gt; SafeString&amp;lt;'TLanguage&amp;gt;
        
        static member Empty : SafeString&amp;lt;'TLanguage&amp;gt; =
            language.Empty() |&amp;gt; SafeString&amp;lt;'TLanguage&amp;gt;.ToType
        
        // takes a string that you certify as representing a fragment in the Language 
        // and returns a corresponding SafeString
        static member Fragment fragment =
            language.LiteralFragment fragment |&amp;gt; SafeString&amp;lt;'TLanguage&amp;gt;.ToType
        
        // takes a string that you certify as representing text and returns a 
        // corresponding SafeString in the Language
        static member Text text =
            language.LiteralText text |&amp;gt; SafeString&amp;lt;'TLanguage&amp;gt;.ToType
            
        static member Join (strings : seq&amp;lt;SafeString&amp;lt;'TLanguage&amp;gt;&amp;gt;) =
          let l = Seq.toList strings
          (foldr language.Add (language.Empty()) l) |&amp;gt; SafeString&amp;lt;'TLanguage&amp;gt;.ToType
        
        static member (+) ((self: SafeString&amp;lt;'TLanguage&amp;gt;), (other: SafeString&amp;lt;'TLanguage&amp;gt;)) : SafeString&amp;lt;'TLanguage&amp;gt; =
            (language.Add self other)  |&amp;gt; SafeString&amp;lt;'TLanguage&amp;gt;.ToType
            
          
    type Xml () =
      // this too is ugly
      static member private ToType (x:#SafeStringBase) =
            x :&amp;gt; SafeStringBase :?&amp;gt; SafeString&amp;lt;Xml&amp;gt;
      static member private ToHashType (x:#SafeStringBase) =
            x :&amp;gt; SafeStringBase :?&amp;gt; #SafeStringBase
            
      interface ILanguage with
        member self.LiteralFragment s = Xml.ToHashType (new SafeString&amp;lt;Xml&amp;gt;(s)) 
        member self.LiteralText s = Xml.ToHashType (new SafeString&amp;lt;Xml&amp;gt;(System.Security.SecurityElement.Escape(s)))
        member self.NativeRepresentation s =
            s.AsString.Replace("&amp;amp;apos;", "'").
              Replace("&amp;amp;quot;", "\"").Replace("&amp;amp;gt;", "&amp;gt;").
              Replace("&amp;amp;lt;", "&amp;lt;").Replace("&amp;amp;amp;", "&amp;amp;")
        member self.Language () = "XML"
        member self.Empty () = Xml.ToHashType (new SafeString&amp;lt;Xml&amp;gt;(String.Empty))
        
        // Don't think we can force this at compile time
        // The best we can do is encapsulate this operation
        member self.Add x y = 
            let left = Xml.ToType x
            let right = Xml.ToType y
            Xml.ToHashType (new SafeString&amp;lt;Xml&amp;gt;(x.AsString + y.AsString)) 
      
    [&amp;lt;EntryPoint&amp;gt;]
    let main a =
        let frag = SafeString&amp;lt;Xml&amp;gt;.Fragment "&amp;lt;em&amp;gt;wow!&amp;lt;/em&amp;gt;"
        printfn "%s" frag.AsString
        let text = SafeString&amp;lt;Xml&amp;gt;.Text "ham &amp;amp; eggs"
        printfn "%s" text.AsString
        
        // We're safe from doing
        //
        // frag + "ham &amp;amp; eggs"
        //
        // Error FS0001: Type constraint mismatch. The type      string     is not compatible 
        // with type     SafeString&amp;lt;Xml&amp;gt;     
        // The type 'string' is not compatible with the type 'SafeString&amp;lt;Xml&amp;gt;'
        
        let sum = frag + text
        printfn "%s" sum.AsString
        0    &lt;/pre&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-4009470264898375864?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/4009470264898375864/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=4009470264898375864' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/4009470264898375864'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/4009470264898375864'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/12/approximate-type-based-solution-to.html' title='An approximate type-based solution to the strings problem'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-3093623456031238801</id><published>2011-12-14T19:58:00.001Z</published><updated>2011-12-14T19:59:30.748Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Deleting files, keeping a few -- in Python</title><content type='html'>&lt;p&gt;And &lt;a href="http://stevegilham.blogspot.com/2011/12/deleting-files-keeping-few.html"&gt;the same again&lt;/a&gt;, only as a script, just because:&lt;/p&gt;&lt;pre class="brush: python"&gt;import glob
import os
  
def fcompare(f1, f2):
  t1 = os.path.getmtime(f1)
  t2 = os.path.getmtime(f2)
  if t1 &amp;lt; t2:
    return -1
  if t1 &amp;gt; t2:
    return 1
  return 0

# TODO: parametrise the pattern and the number to retain
for f in sorted(glob.iglob('*.log'), cmp = fcompare, reverse = True)[3:]:
  os.remove(f)&lt;/pre&gt;&lt;p&gt;If there wasn't that comparison function to write, it'd be terser...&lt;/p&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-3093623456031238801?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/3093623456031238801/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=3093623456031238801' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/3093623456031238801'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/3093623456031238801'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/12/deleting-files-keeping-few-in-python.html' title='Deleting files, keeping a few -- in Python'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-2160053904912983200</id><published>2011-12-13T23:03:00.002Z</published><updated>2011-12-13T23:03:48.430Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='PowerShell'/><title type='text'>Deleting files, keeping a few</title><content type='html'>&lt;p&gt;Inspired by &lt;a href="http://nedbatchelder.com/blog/201112/deleting_files_keeping_a_few.html"&gt;this post&lt;/a&gt;, how it works in PowerShell:&lt;/p&gt;&lt;pre class="brush: c#"&gt;Get-ChildItem *.log | Sort-Object lastWriteTimeUtc -descending | Select-Object -skip 3 | Remove-Item&lt;/pre&gt;&lt;p&gt;will delete all but the most recent 3 files ending in ".log"; and it would be the same sort of thing in any scripting language.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-2160053904912983200?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/2160053904912983200/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=2160053904912983200' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/2160053904912983200'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/2160053904912983200'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/12/deleting-files-keeping-few.html' title='Deleting files, keeping a few'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-4041910386639470189</id><published>2011-12-04T22:51:00.001Z</published><updated>2011-12-04T23:04:04.633Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='weather'/><category scheme='http://www.blogger.com/atom/ns#' term='cycling'/><category scheme='http://www.blogger.com/atom/ns#' term='garden'/><title type='text'>Lovely weather for the time of year</title><content type='html'>&lt;div class="center" style="min-width:260px;border: 1px solid silver;padding:5px;margin:5px;"&gt;&lt;a href="http://www.flickr.com/photos/stevegilham/6454123493/"&gt;&lt;img src="http://farm8.staticflickr.com/7017/6454123493_8eb036d8f9.jpg"  width="500" height="375" alt="The garden today"&gt;&lt;/a&gt; &lt;p&gt;The garden today&lt;/p&gt;&lt;/div&gt;&lt;div class="center" style="min-width:260px;border: 1px solid silver;padding:5px;margin:5px;"&gt;&lt;a href="http://www.flickr.com/photos/stevegilham/5675653955/"&gt;&lt;img src="http://farm6.staticflickr.com/5265/5675653955_238fca2364.jpg"  width="500" height="375" alt="A year ago"&gt;&lt;/a&gt; &lt;p&gt;A year ago&lt;/p&gt;&lt;/div&gt;&lt;p&gt;Yesterday was a mild and sunny day, so I took a spin out in the countryside, stopping at the &lt;a href="http://www.carpentersarmsgastropub.co.uk/"&gt;Carpenters Arms&lt;/a&gt; for a light lunch, of pumpkin and parsnip soup and a ham and cheese toasted sandwich. It was great to enjoy the chance to get out on the bike again -- as it seemed did a number of the lycra crowd, though fortunately they don't seem to hunt in packs at this time of year.&lt;/p&gt;&lt;p&gt;What a contrast with a year ago, when needing to go into town, I wrapped up with long-johns, heavy gloves, multiple layers, goggles, just leaving my nose exposed -- and regretting that for the first couple of miles before getting properly warmed up.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-4041910386639470189?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/4041910386639470189/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=4041910386639470189' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/4041910386639470189'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/4041910386639470189'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/12/lovely-weather-for-time-of-year.html' title='Lovely weather for the time of year'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-4283313778680287751</id><published>2011-11-28T22:15:00.001Z</published><updated>2011-11-28T22:22:14.055Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='PowerShell'/><title type='text'>PowerShell and GTK-Server</title><content type='html'>&lt;p&gt;Another little five-finger exercise, porting the example VBScript stdin driver to PowerShell.  The only annoying thing is that the &lt;code&gt;Start-Process&lt;/code&gt; cmdlet doesn't give you direct access to the streams, so we have to drop into .net to fire up the GTK server process.&lt;/p&gt;&lt;pre class="brush: c#"&gt;&amp;lt;# 
.SYNOPSIS 
    demo to use the GTK-server using STDIN. 
    
.DESCRIPTION 
    This script is based on the VBScript example by
    Peter van Eerten published at http://www.gtk-server.org/demo-stdin.vbs.txt
        
.NOTES 
    File Name  : Try-GTKServer.ps1 
    Requires   : PowerShell Version 1.0

.PARAMETER Help

Show this help text.
#&amp;gt; 
param ( 
    [switch] $Help)

if ($help)
{
    Get-Help $MyInvocation.MyCommand.Definition
    return
}

# hard-coded rather than passed in as a parameter
# I'm feeling lazy tonight...
$gtkpath = "path\to\gtk-server.exe"

## As we want to get at the process stdin, stdout we can't use the Start-Process cmdlet
## $gtkserver = Start-Process -FilePath $gtkpath -ArgumentList "-stdin" -PassThru
## which only allows file I/O
$gtkinfo = new-object System.Diagnostics.ProcessStartInfo @($gtkpath, "-stdin")
$gtkinfo.UseShellExecute = $false
$gtkinfo.RedirectStandardInput = $true
$gtkinfo.RedirectStandardOutput = $true
$gtkinfo.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden

$gtkserver = [System.Diagnostics.Process]::start($gtkinfo)

Function Invoke-GTK 
{
Param([string] $command)
$gtkserver.StandardInput.WriteLine($command)
$line = $gtkserver.StandardOutput.ReadLine()
if ($line -cne "ok") { $line }
}


#Define GUI
Invoke-GTK("gtk_init NULL NULL")
$win = Invoke-GTK("gtk_window_new 0")
Invoke-GTK("gtk_window_set_title $win `"PowerShell Script demo program using STDIN`"")
Invoke-GTK("gtk_widget_set_usize $win 450 400")
$table = Invoke-GTK("gtk_table_new 50 50 1")
Invoke-GTK("gtk_container_add $win $table" )
$button = Invoke-GTK("gtk_button_new_with_label Exit")
Invoke-GTK("gtk_table_attach_defaults $table $button 41 49 45 49")
$entry = Invoke-GTK("gtk_entry_new")
Invoke-GTK("gtk_table_attach_defaults $table $entry 1 40 45 49")
$text = Invoke-GTK("gtk_text_new NULL NULL")
Invoke-GTK("gtk_table_attach_defaults $table $text 1 49 8 44")

$radio1 = Invoke-GTK("gtk_radio_button_new_with_label_from_widget NULL Yes")
Invoke-GTK("gtk_table_attach_defaults $table $radio1 1 10 1 4")
$radio2 = Invoke-GTK("gtk_radio_button_new_with_label_from_widget $radio1 No")
Invoke-GTK("gtk_table_attach_defaults $table $radio2 1 10 4 7")
Invoke-GTK("gtk_widget_show_all $win" )
Invoke-GTK("gtk_widget_grab_focus $entry" )

# message/event loop
do {
    $event = Invoke-GTK("gtk_server_callback wait")
    if ($event -ceq $entry) {
    $tmp = Invoke-GTK("gtk_entry_get_text $entry" )
    if($tmp.Length -gt 1) {
            Invoke-GTK("gtk_text_insert $text NULL NULL NULL `"$tmp`n`" -1")
       }
    # Empty entry field
       Invoke-GTK("gtk_editable_delete_text $entry 0 -1")
    }
} while ($event -cne $button)

Invoke-GTK("gtk_server_exit")&lt;/pre&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-4283313778680287751?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/4283313778680287751/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=4283313778680287751' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/4283313778680287751'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/4283313778680287751'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/11/powershell-and-gtk-server.html' title='PowerShell and GTK-Server'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-1821818361274669907</id><published>2011-11-18T22:15:00.001Z</published><updated>2011-11-18T22:29:05.487Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='PowerShell'/><title type='text'>Another PowerShell egg-timer (using events)</title><content type='html'>&lt;p&gt;Recording a learning exercise for how PowerShell handles events, and how to communicate into event handlers&lt;/p&gt;&lt;p&gt;Note that every registered event must be unregistered (otherwise running the script leaves droppings in your PowerShell session in the form of those claims on events).  The events that are handled -- the timer 250ms wake-ups -- are consumed by being handled; but the one that is merely waited on remains latched after triggering the wait to release and must be explicitly cleared.&lt;/p&gt;&lt;p&gt;Additional AV frills would be the same as any of the &lt;a href="http://poshcode.org/161"&gt;polling loop timer examples&lt;/a&gt; out there.&lt;/p&gt;&lt;pre class="brush: c#"&gt;&amp;lt;# 
.SYNOPSIS 
    This script provides simple egg-timer like functionality. 
    
.DESCRIPTION 
    This script counts down a time interval then speaks some text
        
.NOTES 
    File Name  : Run-Timer.ps1 
    Requires   : PowerShell Version 2.0
    
.PARAMETER Minutes

Delay in minutes

.PARAMETER Seconds

Additional delay in seconds

.PARAMETER Text

What to say at the end of the time

.PARAMETER Help

Show this help text.
#&amp;gt;
param ( 
    [ValidateRange(0,59)] [int] $Minutes,
    [ValidateRange(0,59)] [int] $Seconds,
    [string] $Text="It is time",
    [switch] $Help)

if ($help)
{
    Get-Help $MyInvocation.MyCommand.Definition
    return
}

$delay = new-object "system.timespan" @(0, $Minutes, $Seconds)
$fireAt = [System.DateTime]::UtcNow + $delay
Write-Host "Requested alarm time = `t$fireAt"

# Do the heavy lifting up front
$Voice = new-object -com SAPI.SpVoice
$Voice.Rate = -5
$timer = new-object "system.timers.timer" @(250.0)
$timer.autoreset = $true

$handler =  {
    # extract arguments (no closures here)
    $fireAt = $Event.MessageData
    $timer = $Sender
  
    # round time to wait to integer seconds and display
    $delta = $fireAt - [System.DateTime]::UtcNow
    $delta = new-object "system.timespan" @(0, $delta.Minutes, [System.Math]::Round($delta.Seconds))
    [System.Console]::Write("$delta`r")
  
    # After the time allotted, kill the timer
    if ([System.DateTime]::UtcNow -gt $fireAt) { 
        $timer.enabled = $false
        $timer.autoreset = $false
        $timer.Dispose() 
    }
}

Register-ObjectEvent -InputObject $timer -EventName Elapsed -SourceIdentifier "Timer.Elapsed" -Action $handler -MessageData $fireAt | Out-Null
Register-ObjectEvent -InputObject $timer -EventName Disposed -SourceIdentifier "Timer.Disposed"

$timer.Start() 

# Wait until done
if (-not (Wait-Event "Timer.Disposed" -Timeout 3600 )) {
    Write-Host "Timed out"
    $timer.Stop() 
    $timer.Dispose()
} else {
    Write-Host "Completed"
}
Write-Host "Actual alarm time = `t$([System.DateTime]::UtcNow)"

# Tidy event registrations and event queue
# The Elapsed events are spent in the handler
# but the one we wait on must be explicitly cleared
Remove-Event "Timer.Disposed"
Unregister-Event "Timer.Elapsed"
Unregister-Event "Timer.Disposed"

#audible alarm
$Voice.Speak($Text) | Out-Null
Write-Host "`r`nDone"&lt;/pre&gt;&lt;br /&gt;
&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-1821818361274669907?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/1821818361274669907/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=1821818361274669907' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/1821818361274669907'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/1821818361274669907'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/11/another-powershell-egg-timer-using.html' title='Another PowerShell egg-timer (using events)'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-8947453711912974928</id><published>2011-11-06T23:41:00.001Z</published><updated>2011-11-07T15:30:37.769Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='functional programming'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>C# : The null object pattern and the poor man's Maybe Monad</title><content type='html'>&lt;p&gt;Lots of people have re-invented the Maybe monad in C#, as a simple search will show,  usually as a way of bypassing null checking &lt;code&gt;if&lt;/code&gt; statements and to be able &lt;a href="http://smellegantcode.wordpress.com/2008/12/11/the-maybe-monad-in-c/"&gt;to write code like&lt;/a&gt;&lt;/p&gt;&lt;pre class="brush: c#"&gt;return Music.GetCompany("4ad.com")
            .IfNotNull(company =&amp;gt; company.GetBand("Pixies"))
            .IfNotNull(band =&amp;gt; band.GetMember("David"))
            .IfNotNull(member =&amp;gt; member.Role);&lt;/pre&gt;&lt;p&gt;But we  have the tools given to us already if we observe that &lt;i&gt;&lt;code&gt;null&lt;/code&gt; has no type&lt;/i&gt; -- as we can see when&lt;/p&gt;&lt;pre class="brush: c#"&gt;var x = new string[1];  // default null value
            var n = x.OfType&amp;lt;string&amp;gt;().Count();
            Console.WriteLine("Length = {0}", n);&lt;/pre&gt;&lt;p&gt;reports a length of zero!&lt;/p&gt;&lt;p&gt;So our &lt;code&gt;Some&amp;lt;T&amp;gt;&lt;/code&gt; is just a length-1 &lt;code&gt;IEnumerable&amp;lt;T&amp;gt;&lt;/code&gt;; and &lt;code&gt;None&amp;lt;T&amp;gt;&lt;/code&gt; an empty one -- the null object pattern, in fact.&lt;/p&gt;&lt;p&gt;For the price of having to specify types at each stage -- as we would anyway have had to in the past for declaring intermediate the values to test against null -- we can use existing methods to finesse the null check.  Returning to the borrowed example, it would be&lt;/p&gt;&lt;pre class="brush: c#"&gt;return new[] { Music.GetCompany("4ad.com") }
            .OfType&amp;lt;RecordCompany&amp;gt;().Select(company =&amp;gt; company.GetBand("Pixies"))
            .OfType&amp;lt;Band&amp;gt;().Select(band =&amp;gt; band.GetMember("David"))
            .OfType&amp;lt;Member&amp;gt;().Select(member =&amp;gt; member.Role);&lt;/pre&gt;&lt;p&gt;The &lt;strike&gt;monadic return&lt;/strike&gt; &lt;ins&gt;monad execution&lt;/ins&gt; operation is just &lt;code&gt;FirstOrDefault()&lt;/code&gt;; which is fine for reference types.  Value types are different in any case as we wouldn't be null checking those -- as the later steps in a chain though, stopping at the filtering to the value type and making an &lt;code&gt;Any&lt;/code&gt; test may be preferred.&lt;/p&gt;&lt;p&gt;Looking at value types, switching to &lt;a href="http://mikehadlow.blogspot.com/2011/01/monads-in-c-5-maybe.html"&gt;this example&lt;/a&gt;, the code looks like:&lt;/p&gt;&lt;pre class="brush: c#"&gt;public static IEnumerable&amp;lt;int&amp;gt; Div(this int numerator, int denominator)
        {
            //return denominator == 0
            //    ? Enumerable.Empty&amp;lt;int&amp;gt;()
            //    : new[] { numerator/denominator };
            if (denominator != 0) yield return numerator/denominator;
        }

        public static IEnumerable&amp;lt;int&amp;gt; DoSomeDivision(int denominator)
        {
            return from a in 12.Div(denominator)
                   from b in a.Div(2)
                   select b;
        }
...
            var result = 
                from a in new[] { "Hello World!" }
                from b in DoSomeDivision(2)
                from c in new[] { new DateTime(2010, 1, 14) }
                select (a + " " + b.ToString() + " " + c.ToShortDateString());
            Console.WriteLine(result.Any() ? result.First() : "Nothing");&lt;/pre&gt;&lt;p&gt;which leaks a little bit in that we have to do the &lt;strike&gt;monadic return&lt;/strike&gt; &lt;ins&gt;monad execution&lt;/ins&gt; by hand at the end, but otherwise behaves exactly as the explicit monad implementation.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-8947453711912974928?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/8947453711912974928/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=8947453711912974928' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/8947453711912974928'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/8947453711912974928'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/11/c-null-object-pattern-and-poor-mans.html' title='C# : The null object pattern and the poor man&apos;s Maybe Monad'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-8676408608356930900</id><published>2011-10-31T23:17:00.001Z</published><updated>2011-10-31T23:17:19.049Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='cycling'/><title type='text'>3070.1</title><content type='html'>&lt;p&gt;That's the cycle odo at the end of the month, well over my stretch goal for the month of hitting of 3000.  The question now is how far over 3100 by the end of the year.&lt;/p&gt;&lt;p&gt;And then to see what I can do by the end of next June : in order of difficulty, 2000 miles in the year, to 4000 recorded, to 1000 miles in the half year.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-8676408608356930900?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/8676408608356930900/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=8676408608356930900' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/8676408608356930900'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/8676408608356930900'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/10/30701.html' title='3070.1'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-7512369560590877397</id><published>2011-10-29T23:33:00.000+01:00</published><updated>2011-10-29T23:33:21.049+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><title type='text'>F# -- where has our Roslyn been?</title><content type='html'>&lt;p&gt;The big thing about the new Roslyn CTP is that it's a C# compiler built in C#, so accessible simply from C# code via a standardized set of APIs; including ones that permit parse tree rewriting (and hence DIY language extensions) as well as giving a more official mechanism for such things as source-level inspection (e.g. StyleCop).  Indeed, the lack of any such managed language service for VB is a reason that there's no StyleCop equivalent for the other major managed language.&lt;/p&gt;&lt;p&gt;F# has had the managed compiler from the start -- but presumably (as I've not noticed anything being done with it by way of meta-programming or suggesting coding conventions in the manner of StyleCop) not the suitable APIs.  There is a project or several here for someone to take up.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-7512369560590877397?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/7512369560590877397/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=7512369560590877397' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/7512369560590877397'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/7512369560590877397'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/10/f-where-has-our-roslyn-been.html' title='F# -- where has our Roslyn been?'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-2719976468509380210</id><published>2011-10-29T22:23:00.000+01:00</published><updated>2011-10-29T22:34:36.164+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><title type='text'>F# under the covers XIV -- Match expressions : Does my complexity look big in this?</title><content type='html'>&lt;p&gt;The code generation in F# in debug builds is fond of adding plenty of extra branch points that will confuse any naive computation of code complexity based on counting branch instructions at the IL level.  Let us take the innocent method&lt;/p&gt;&lt;pre class="brush: f#"&gt;let function alist [...other args...] =
    match alist with
    | h :: tail -&amp;gt; // some computation
    | [] -&amp;gt; []&lt;/pre&gt;&lt;p&gt;The instructions that map to the &lt;code&gt;match alist with&lt;/code&gt; context include&lt;/p&gt;&lt;pre class="brush: c#"&gt;IL_0004: call instance class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1&amp;lt;!0&amp;gt; class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1&amp;lt;class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2&amp;lt;...`1&amp;lt;float64&amp;gt;&amp;gt;&amp;gt;::get_TailOrNull()
 IL_0009: brtrue.s IL_000d
 IL_000b: br.s IL_0044
 IL_000d: ldloc.0&lt;/pre&gt;&lt;p&gt;where the first branch could be replaced with a &lt;code&gt;brfalse.s IL_0044&lt;/code&gt; and the code at offsets 0xb, 0xc replaced by &lt;code&gt;nop&lt;/code&gt;s with no change in the semantics -- but as given it adds an extra cycle to the flow of control graph, and hence false complexity.&lt;/p&gt;&lt;p&gt;The release build of the code retains the first two instructions, but then rather than jumping to get and return an empty list, it in-lines that fraction of the code.  If we look at it in C#, the release build looks like&lt;/p&gt;&lt;pre class="brush: c#"&gt;FSharpList&amp;lt;...&amp;gt; function(FSharpList&amp;lt;...&amp;gt; alist, ...)
{
         if (plist.TailOrNull == null)
         {
            return FSharpList&amp;lt;...&amp;gt;.Empty;
         }
        // some computation&lt;/pre&gt;&lt;p&gt;where the debug version has a &lt;code&gt;goto&lt;/code&gt; in that &lt;code&gt;if&lt;/code&gt; clause that jumps to a statement that returns the empty list.&lt;/p&gt;&lt;p&gt;As an aside, it's also worth noting that nowhere in the .pdb information is there a map to any of the match expressions : there is never a source context which matches the &lt;code&gt;| h :: tail -&amp;gt;&lt;/code&gt; or &lt;code&gt;| [] -&amp;gt;&lt;/code&gt;; it's all collapsed into the &lt;code&gt;match ... with&lt;/code&gt; fragment.&lt;/p&gt;&lt;p&gt;For more complex match expressions such as&lt;/p&gt;&lt;pre class="brush: f#"&gt;match context with
          | :? Type1 as t1 -&amp;gt; // type 1
          | :? Type2 as t2 -&amp;gt; // type 2
          | :? Type3 as t3 -&amp;gt; // type 3 
          | other -&amp;gt; // something else &lt;/pre&gt;&lt;p&gt;the debug IL organizes into trying each of the branch criteria in turn, then jumping ahead to the matched expression; this ends up with a series of redundant branches like&lt;/p&gt;&lt;pre class="brush: c#"&gt;IL_001f: brfalse.s IL_0023
 IL_0021: br.s IL_003f
 IL_0023: ldloc.0&lt;/pre&gt;&lt;p&gt;culminating in a final triple burst&lt;/p&gt;&lt;pre class="brush: c#"&gt;IL_0039: brfalse.s IL_003d
 IL_003b: br.s IL_0077
 IL_003d: br.s IL_0085
 IL_003f: ldloc.1&lt;/pre&gt;&lt;p&gt;whereas the release build treats it more as an &lt;code&gt;if..elseif..elseif..else&lt;/code&gt; construct.&lt;/p&gt;&lt;p&gt;&lt;a href="http://stevegilham.blogspot.com/2011/10/computing-cyclomatic-complexity-with.html"&gt;As I noted yesterday&lt;/a&gt;, this means that there can be a factor of at least two difference in the naively computed cyclomatic complexity of a method depending whether the release or debug build is chosen.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-2719976468509380210?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/2719976468509380210/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=2719976468509380210' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/2719976468509380210'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/2719976468509380210'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/10/f-under-covers-xiv-match-expressions.html' title='F# under the covers XIV -- Match expressions : Does my complexity look big in this?'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-3928412784965156352</id><published>2011-10-28T16:55:00.000+01:00</published><updated>2011-11-18T22:31:28.269Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='test automation'/><category scheme='http://www.blogger.com/atom/ns#' term='PowerShell'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Computing cyclomatic complexity with PowerShell and FxCop</title><content type='html'>&lt;p&gt;A measure of the cyclomatic complexity of a .net method can be gauged by counting the number of IL branch instructions that do nor branch to the next instruction, and which have a distinct target from any other branch -- this is essentially the algorithm used in NDepend 1.x to make the computation. The introspection mechanism of FxCop provides enough decompilation of the IL that getting the instruction type and offset, and the target offset of a branch.  An actual FxCop rule, though feasible to write, however, would be less useful than one could hope. &lt;/p&gt;&lt;p&gt;For one thing, FxCop itself doesn't provide any convenient way of passing parameters to custom rules (to e.g. set a trigger threshold); and for another, any analysis is likely to be run over a debug build (DEBUG and CODE_ANALYSIS variables defined there to not contaminate the released code with [SuppressMessage] annotations), which is likely to have a different underlying complexity to a release build (this is especially true in F# debug builds where there are a lot of sequences that look like&lt;/p&gt;&lt;pre class="brush: c#"&gt;&amp;nbsp;&amp;nbsp;IL_00ec: brtrue.s IL_00f0
  IL_00ee: br.s IL_00f2
  IL_00f0: br.s IL_010a
  IL_00f2: ...&lt;/pre&gt;&lt;p&gt;which can be replaced by&lt;/p&gt;&lt;pre class="brush: c#"&gt;&amp;nbsp;&amp;nbsp;IL_00ec: brtrue IL_010a
  [enough nop opcodes]
  IL_00f2: ...&lt;/pre&gt;&lt;p&gt;which, as it turns out, is pretty much what the release build does.&lt;/p&gt;&lt;p&gt;A more configurable and flexible way of performing the operation is to drive the FxCop facilities from a script -- these days I'm doing a lot of my .net scripting in PowerShell, but it could equally well be done with IronPython or similar .net scripting language.  The result looks like this:&lt;/p&gt;&lt;pre class="brush: c#"&gt;&amp;lt;# 
.SYNOPSIS 
    This script estimates cyclomatic complexities over a folder full of assemblies. 
    
.DESCRIPTION 
    It loads the assemblies, and then introspeccts over each method, estimating the 
    complexity as number of branch instructions which do not target the next instruction
    and which have a unique target instruction.
        
.NOTES 
    File Name  : Get-Complexity.ps1 
    Requires   : PowerShell Version 2.0 (3.0 or alternative launcher for .net 4/FxCop 10.0)
    
.PARAMETER FxCopPath

The path to the directory where FxCop has been installed

.PARAMETER AssemblyPath

The path to the folder containging the assemblies to inspect

.PARAMETER ReportLevel

If given, print out methods matching or exceeding this complexity,
otherwise return the whole analysis to the pipeline
#&amp;gt; 
param ( 
    [string] $FxCopPath,
    [Parameter(Mandatory = $true)] [string] $AssemblyPath,
    [int] $ReportLevel,
    [switch] $Help)

$fxcopVersion = @{ "v2"='Microsoft FxCop 1.36'; "v4"='Microsoft FxCop 10.0' }

if (-not $FxCopPath) {
  $programFiles = [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::ProgramFiles)
  $dotNetVersion = [System.Runtime.InteropServices.RuntimeEnvironment]::GetSystemVersion().Split('.') | Select-Object -First 1
  $FxCopPath = Join-Path -Path $programFiles -ChildPath ($fxcopVersion[$dotNetVersion])
}

if ($help -or (-not (Test-Path $FxCopPath -PathType Container)) -or 
              (-not (Test-Path $AssemblyPath -PathType Container)) -or
              (-not (Test-Path $(Join-Path -Path $FxCopPath -ChildPath 'FxCopSdk.dll') -PathType Leaf)))
{
    Get-Help $MyInvocation.MyCommand.Definition
    return
}

Add-Type -Path $(Join-Path -Path $FxCopPath -ChildPath 'FxCopSdk.dll')

## IL branch opcodes
$branchOpCodes =  @([Microsoft.FxCop.Sdk.OpCode]::Beq, 
    [Microsoft.FxCop.Sdk.OpCode]::Beq_S, 
    [Microsoft.FxCop.Sdk.OpCode]::Bge,
    [Microsoft.FxCop.Sdk.OpCode]::Bge_S, 
    [Microsoft.FxCop.Sdk.OpCode]::Bge_Un, 
    [Microsoft.FxCop.Sdk.OpCode]::Bge_Un_S, 
    [Microsoft.FxCop.Sdk.OpCode]::Bgt,
    [Microsoft.FxCop.Sdk.OpCode]::Bgt_S, 
    [Microsoft.FxCop.Sdk.OpCode]::Bgt_Un, 
    [Microsoft.FxCop.Sdk.OpCode]::Bgt_Un_S, 
    [Microsoft.FxCop.Sdk.OpCode]::Ble, 
    [Microsoft.FxCop.Sdk.OpCode]::Ble_S, 
    [Microsoft.FxCop.Sdk.OpCode]::Ble_Un, 
    [Microsoft.FxCop.Sdk.OpCode]::Ble_Un_S, 
    [Microsoft.FxCop.Sdk.OpCode]::Blt, 
    [Microsoft.FxCop.Sdk.OpCode]::Blt_S, 
    [Microsoft.FxCop.Sdk.OpCode]::Blt_Un, 
    [Microsoft.FxCop.Sdk.OpCode]::Blt_Un_S, 
    [Microsoft.FxCop.Sdk.OpCode]::Bne_Un, 
    [Microsoft.FxCop.Sdk.OpCode]::Bne_Un_S, 
    [Microsoft.FxCop.Sdk.OpCode]::Br,
    [Microsoft.FxCop.Sdk.OpCode]::Br_S,
    [Microsoft.FxCop.Sdk.OpCode]::Brtrue,
    [Microsoft.FxCop.Sdk.OpCode]::Brtrue_S,
    [Microsoft.FxCop.Sdk.OpCode]::Brfalse, 
    [Microsoft.FxCop.Sdk.OpCode]::Brfalse_S)

# Compute method complexity (based on the algorithm of NDepend 1.3.2 by Patrick Smacchia)
Function Compute-Complexity ([Microsoft.FxCop.Sdk.InstructionCollection] $body)
{
  $offsets = @()
  $body | ? { $branchOpCodes -contains $_.OpCode } | ? {
                $_.Value -ne ($_.Offset + 2) } | ? { ## don't count branch to next instruction
                  -not ($offsets -contains $_.Offset) }  | % {
                    $offsets += $_.Offset } | Out-Null
  $offsets.Length
}

# load assemblies (no symbols needed) and explore methods
$assemblies = dir $AssemblyPath | ? { $_.Name.EndsWith(".exe") -or $_.Name.EndsWith(".dll") }  | % { 
        $assembly = [Microsoft.FxCop.Sdk.AssemblyNode]::GetAssembly($_.FullName)
        $compilerGenerated = $assembly.GetType([Microsoft.FxCop.Sdk.Identifier]::For("System.Runtime.CompilerServices"), [Microsoft.FxCop.Sdk.Identifier]::For("CompilerGeneratedAttribute"), $true)
        New-Object PSObject -Property @{   
        Name = $_.Name;
        Types = $assembly.Types | % {
            New-Object PSObject -Property @{ 
                Name = $_.FullName;
                Methods = $_.Members | ? { $_.NodeType -eq [Microsoft.FxCop.Sdk.NodeType]::Method } | ? {
                    -not (($_.Attributes | % { $_.Type }) -contains $compilerGenerated) } | % { ## skip compiler generated code
                    New-Object PSObject -Property @{ 
                        Name = $_.GetUnmangledNameWithTypeParameters();
                        Complexity = Compute-Complexity($_.Instructions);  }}}}}}

if ($ReportLevel) { 
$assemblies | % { $aname = $_.Name
$_.Types | % { $cname = "`t$($_.Name)"
$_.Methods | ? { $_.Complexity -ge $ReportLevel } | % {
if ($aname) { Write-Host $aname; $aname = $null }
if ($cname) { Write-Host $cname; $cname = $null }
Write-Host "`t`t$_" }}}} else { $assemblies }&lt;/pre&gt;&lt;p&gt;and running it over a set of F# code shows that the reported complexity of the release build of a method halves (or more) what is reported for a debug build.  I haven't made a comparison over an significant amount of C# as yet, to see how much complexity the compiler removes between the two configurations.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-3928412784965156352?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/3928412784965156352/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=3928412784965156352' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/3928412784965156352'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/3928412784965156352'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/10/computing-cyclomatic-complexity-with.html' title='Computing cyclomatic complexity with PowerShell and FxCop'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-524168883806618718</id><published>2011-10-26T19:42:00.000+01:00</published><updated>2011-11-07T00:00:23.950Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='cycling'/><title type='text'>End of Season Cycling</title><content type='html'>&lt;iframe width="425" height="400" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="http://maps.google.co.uk/maps/ms?vpsrc=6&amp;amp;ctz=-60&amp;amp;ie=UTF8&amp;amp;msa=0&amp;amp;msid=214190258906006153051.0004b0368e072284f101f&amp;amp;t=m&amp;amp;ll=52.096382,0.942078&amp;amp;spn=0.337465,0.585022&amp;amp;z=10&amp;amp;output=embed"&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;small&gt;View &lt;a href="http://maps.google.co.uk/maps/ms?vpsrc=6&amp;amp;ctz=-60&amp;amp;ie=UTF8&amp;amp;msa=0&amp;amp;msid=214190258906006153051.0004b0368e072284f101f&amp;amp;t=m&amp;amp;ll=52.096382,0.942078&amp;amp;spn=0.337465,0.585022&amp;amp;z=10&amp;amp;source=embed" style="color:#0000FF;text-align:left"&gt;Autumn 2011&lt;/a&gt; in a larger map&lt;/small&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;The autumn having settled to being bright, mild and breezy, a last chance to have a cycle break before winter -- indeed I was apparently the last customer of the season at Suffolk Cyclebreaks; going through the south of Suffolk and the north of Essex, which I haven't done as much as places further north in the region.&lt;/p&gt;&lt;p&gt;An early start on Sunday took me most of the way past Ipswich by shortly after midday; though having been pointed at the restaurant at &lt;a href="http://www.suffolkfoodhall.co.uk/"&gt;Wherstead Hall&lt;/a&gt;, I'd aimed for that, rather than again to the &lt;a href="http://www.debeninns.co.uk/buttandoyster/"&gt;Butt and Oyster&lt;/a&gt; at Pin Mill, taking the bridlepath past &lt;a href="http://www.jimmysfarm.com/"&gt;Jimmy's Farm&lt;/a&gt; to cut the corner, and only have to do a little way along the main road until diving off into Wherstead village.  The concrete track past the church brings you to the "hall" -- a large modern building, housing a local produce market, garden centre, and restaurant serving locally sourced food -- for me, a cheeseburger with local beef and cheese, plus a bottle of &lt;a href="http://www.calvors.co.uk/lager/premium-lager.php"&gt;the local lager&lt;/a&gt;.&lt;br /&gt;
&lt;/p&gt;&lt;div class="center" style="min-width:260px;border: 1px solid silver;padding:5px;margin:5px;"&gt;&lt;a href="http://www.flickr.com/photos/stevegilham/6283645466/" title="A14 bridge over the Orwell by Steve Gilham, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6120/6283645466_6dd7ce7ae8.jpg" width="500" height="375" alt="A14 bridge over the Orwell"&gt;&lt;/a&gt; &lt;p&gt;A14 bridge over the Orwell&lt;/p&gt;&lt;/div&gt;&lt;p&gt;From there, I carried on along the coast, deciding as the hour was advancing (past 1:30, but in the low sun, feeling later) not to detour to the Butt and Oyster for more beer; but carried on, until suddenly happening upon the Baker's Arms at Harkstead, which advertised food still being served, let alone beer, so had a pint of Wherry, before carrying on.  The official route &lt;a href="http://stevegilham.blogspot.com/2008/08/cycling-holiday-day-1.html"&gt;as I took it last&lt;/a&gt; weaves a long way to fit through crossings of the Stour and the A12.  This time I decided that being a quiet Sunday afternoon, the main road through Manningtree would be a simpler route, arriving at Dedham by 4pm -- and the Sun Inn was open, and serving amongst others, Arizona Bitter (Phoenix Brewery) and Wolf Bitter (Wolf Brewery).  Thus fortified, I reached &lt;a href="http://www.milsomhotels.com/milsoms/"&gt;Milsoms&lt;/a&gt; (which in the last three years has realised that it makes no more sense to charge separately for wifi than it does for hot water).&lt;/p&gt;&lt;p&gt;Dinner was bresaola, king prawn and green mango curry, and lemon cheesecake, with lots of mineral water; and then with the windows closed in the cool night, not even the steady drone of traffic on the A12 disturbed me.&lt;/p&gt;&lt;p&gt;Morning, fortified with a full English, etc, I decided to avoid the rather unpleasant country between Dedham and Stoke-by-Nayland, and head rather directly to Hadleigh (where I got tricked by the cyclepath again), pushed up the long grind after crossing the A1071, then with most of the pubs mentioned in the guide being closed Mondays, struck straight cross country to Sudbury (cadging a coffee from my surprised parents), before taking a loop west on the way to the night's rest at Lavenham.&lt;/p&gt;&lt;p&gt;Partly guided by the OS map's PH markers, I struck through Glemsford, to find the first two pubs closed at lunchtime, and as it was close upon 2pm, I was despairing of finding a place to pause, when suddenly, the Cherry Tree Inn "Open all day for quaff and scoff".  There was one other customer there when I went in, but more regulars drifted in as I committed a couple of pints of shandy, and I ended up joining in the attack on the Times' crossword.&lt;/p&gt;&lt;p&gt;Then when I could justify hanging around indoors on a glorious sunny afternoon no longer, made the last leg to Lavenham, parking up at the &lt;a href="http://www.wheelersangel.com/"&gt;Angel&lt;/a&gt;.  I took a bit of a wander around, including a pint at the Greyhound (where a greyhound wandered out from behind the bar and looked for attention), before dining at the hotel -- potted duck with Agen prunes, shepherds' pie with peas and spinach, then the cheeseboard; which I was ready for, having not actually felt the need for lunch.&lt;/p&gt;&lt;p&gt;The rain, which long range forecasts had suggested might wash the whole holiday out, went over in the night, leaving it somewhat raw but otherwise fair cycling weather in the morning.  Approaching Gedding, there were some diversion signs, and I saw that the route I was planning to take was marked as road closed.  But then I saw that the other end of the closure was a few yards away, and between, just a fenced off hole with more than ample room to cycle past; so I went on as I intended, until just after midday, when the Brewer's Arms called.&lt;/p&gt;&lt;p&gt;A burger of local red poll beef, and a pint later, and the sheet of cloud that had been hanging overhead all morning gone, I decided to amble north of the A14, then loop through the lanes back to Alder Carr farm.  This time I ran into a rather more serious road closure on the approved route, but fortunately I could back off and take a separate cycle path through the new development.&lt;/p&gt;&lt;p&gt;And then, shortly after crossing the A1120, a sudden rattle rattle pop! as a bulge near the valve on the back wheel finally wore through and the inner-tube burst spectacularly.  At least it was warm and sunny, and the road quiet, as I replaced the inner tube, moved the tyre around, and pumped up to do the last couple of miles.  I suspect the tyre may be a little narrow for the wheel, as the new tube is also bulging in the same way.  Still, now it'll soon be past the end of BST, and cycling to work, I can put it in for a service and get everything replaced that needs it.&lt;/p&gt;&lt;p&gt;With all this under the wheels, my odo is now well over the 3000 mile mark, getting me well past my stretch goal for the month.&lt;/p&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-524168883806618718?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/524168883806618718/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=524168883806618718' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/524168883806618718'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/524168883806618718'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/10/end-of-season-cycling.html' title='End of Season Cycling'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://farm7.static.flickr.com/6120/6283645466_6dd7ce7ae8_t.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-8654092278951092090</id><published>2011-10-18T23:35:00.002+01:00</published><updated>2011-10-18T23:35:55.470+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='cycling'/><title type='text'>2908.4</title><content type='html'>&lt;p&gt;Back &lt;a href="http://stevegilham.blogspot.com/2011/06/target-achieved.html"&gt;at the end of June&lt;/a&gt;, I was hoping to have managed another 1000 miles on my bike by the end of the year.&lt;/p&gt;&lt;p&gt;Tonight getting home, on a clear if breezy day, the odo was showing 2908.4, or 1001.6 miles since then.  The question now becomes whether I can roll it over the 3000 mile mark before the end of the month, let alone the end of the year!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-8654092278951092090?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/8654092278951092090/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=8654092278951092090' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/8654092278951092090'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/8654092278951092090'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/10/29084.html' title='2908.4'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-9024178992884859996</id><published>2011-10-18T23:24:00.000+01:00</published><updated>2011-10-18T23:30:58.073+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='PowerShell'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Harnessing the PowerShell command line parser in .net</title><content type='html'>&lt;p&gt;One of the annoying niggles about the .net framework is the lack of an intrinsic command line parser -- yes, there's Mono.Options or the F# power-pack, but they're not always just there to hand.  PowerShell is there on Win7 and up, and is likely to be there on machines with older OS versions in a suitably technical environment,  So if you just want to whip up a simple command line tool, and you're not writing it in PowerShell, why not at least borrow its features?&lt;/p&gt;&lt;p&gt;First, we want to get the argument list as provided -- for this we have to get the semi-raw line via Win32 (semi-raw as the shell will already have processed escapes -- in PowerShell that means that backticks, semi-colons and double-dashes are significant):&lt;/p&gt;&lt;pre class="brush: f#"&gt;module CommandLineParser =

    module private NativeMethods = 
        // Retrieves the command line as processed by the shell
        [&amp;lt;DllImport("kernel32.dll", CharSet = CharSet.Auto)&amp;gt;]
        extern System.IntPtr GetCommandLine();
    
    // Remove the executable name (first token) from the command-line
    let ExtractCommandLineArgumentString() =
        let ptr = NativeMethods.GetCommandLine();
        let commandLine = Marshal.PtrToStringAuto(ptr);
        
        let result = PSParser.Tokenize(commandLine)
        commandLine.Substring( (fst result).[0].EndColumn )&lt;/pre&gt;&lt;br /&gt;
&lt;p&gt;This uses the PowerShell tokenizer to strip out the executable name.  We also want to be able to describe what the command line parameters are like, and other usage text&lt;/p&gt;&lt;pre class="brush: f#"&gt;type Switch = {
         Name : string;
         Usage : string }
        
    type Value = {
         Name : string;
         Required : bool;
         Type : Type;
         Usage : string }
         
    type Argument = 
     | Switch of Switch
     | Value of Value
     
    type Application = {
         Name : string;
         Synopsis : string;
         Description : seq&amp;lt;string&amp;gt;;
         Arguments : seq&amp;lt;Argument&amp;gt; }&lt;/pre&gt;&lt;br /&gt;
&lt;p&gt;Then we can build a PowerShell function that takes the parameters we've defined, and just places them in a hash : this is the meat of the exercise&lt;/p&gt;&lt;pre class="brush: f#"&gt;// Build a simple PowerShell function with the command line we want
    let GenerateFunction (app : Application) =
        let preamble = String.Join("\r\n", [| "&amp;lt;#"; 
            ".SYNOPSIS ";
            app.Synopsis;
            "   "; 
            ".DESCRIPTION \r\n"; |]) + String.Join("\r\n\r\n", Seq.toArray app.Description) + "\r\n\r\n"
        let usage = String.Join("\r\n", 
                            app.Arguments 
                            |&amp;gt; Seq.map(fun a -&amp;gt; match a with
                                                | Switch {Name=name; Usage=usage} -&amp;gt; (name, usage)
                                                | Value {Name=name; Usage=usage} -&amp;gt; (name, usage))
                            |&amp;gt; Seq.map (fun (name, usage) -&amp;gt; String.Format(System.Globalization.CultureInfo.InvariantCulture,
                                                                           ".PARAMETER {0}\r\n\r\n{1}\r\n",
                                                                           name, usage))
                            |&amp;gt; Seq.toArray) + "\r\n"                                                     
        let decl = String.Join("\r\n", [| "#&amp;gt;" ;
            "function Parse-CommandLine";
            "{"; 
            "  param ( " |]) + "\r\n"
            
        let prefix (isMandatory : bool) =
            if isMandatory then
                "[Parameter(Mandatory=$true)]"
            else
                String.Empty
         
        let parameters = String.Join(",\r\n", 
                            app.Arguments 
                            |&amp;gt; Seq.map(fun a -&amp;gt; match a with
                                                | Switch {Name=name} -&amp;gt; (name, "switch", false)
                                                | Value {Name=name; Type=``type``; Required=required} -&amp;gt; (name, ``type``.FullName, required))
                            |&amp;gt; Seq.map (fun (name, ``type``, required) -&amp;gt; String.Format(System.Globalization.CultureInfo.InvariantCulture,
                                                                                   "    {2} [{1}] ${0}",
                                                                                   name, ``type``,
                                                                                   prefix required))
                            |&amp;gt; Seq.toArray) + "\r\n"                                               
        let body = String.Join("\r\n", [| ")";
            "   @{" |]) + "\r\n"
        let assignment = String.Join(";\r\n", 
                            app.Arguments 
                            |&amp;gt; Seq.map(fun a -&amp;gt; match a with
                                                | Switch {Name=name} -&amp;gt; name
                                                | Value {Name=name} -&amp;gt; name)
                            |&amp;gt; Seq.map (fun name -&amp;gt; String.Format(System.Globalization.CultureInfo.InvariantCulture,
                                                                                   "    {0}=${0}", name))
                            |&amp;gt; Seq.toArray) + "\r\n"                                               
        let coda =  String.Join("\r\n", [| "'$args' = $args" ;
            "    }";
            "}" |])
            
        preamble + usage + decl + parameters + body + assignment + coda&lt;/pre&gt;&lt;br /&gt;
&lt;p&gt;For producing a usage description, we can ask PowerShell for one, flatten it to a sequence of strings, and then strip out the unwanted PowerShell-isms&lt;/p&gt;&lt;pre class="brush: f#"&gt;let Runspace = new RunspaceInvoke()
    
    let RunScript (script: string) =
        Runspace.Invoke(script)
        
    let Usage (app : Application) =
        let text = (GenerateFunction app) + 
                    "\r\n$a = Get-Help Parse-CommandLine -full\r\n" +
                    "$a.Name = \"" + app.Name + "\"\r\n" + // replace the fake name
                    "$a.details.name = \"" + app.Name + "\"\r\n" + // replace the fake name
                    "$a.Syntax.SyntaxItem.Name = \"" + app.Name + "\"\r\n" + // replace the fake name
                    "$a | Out-String -Stream"
       
        let result = RunScript(text)
        (result |&amp;gt; Seq.map (fun p -&amp;gt; p.BaseObject)).OfType&amp;lt;string&amp;gt;() 
        |&amp;gt; Seq.takeWhile (fun l -&amp;gt; l.Trim() &amp;lt;&amp;gt; "&amp;lt;CommonParameters&amp;gt;")
        |&amp;gt; Seq.map (fun l -&amp;gt; l.Replace("[&amp;lt;CommonParameters&amp;gt;]", String.Empty))
        |&amp;gt; Seq.filter (fun l -&amp;gt; not &amp;lt;| l.Trim().StartsWith("Accept pipeline input?", StringComparison.Ordinal))
        |&amp;gt; Seq.filter (fun l -&amp;gt; not &amp;lt;| l.Trim().StartsWith("Accept wildcard characters?", StringComparison.Ordinal))
        |&amp;gt; Seq.filter (fun l -&amp;gt; not &amp;lt;| l.Trim().StartsWith("Default value", StringComparison.Ordinal))
        |&amp;gt; Seq.iter (fun x -&amp;gt; Console.WriteLine("{0}", x))&lt;/pre&gt;&lt;br /&gt;
&lt;p&gt;Finally, we can parse a supplied command-line, writing a failure reason and usage info if things fail&lt;p&gt;&lt;br /&gt;
&lt;pre class="brush: f#"&gt;let Parse (app : Application) (commandline : string) =
        let text = (GenerateFunction app) + 
                      "\r\nParse-CommandLine " + commandline + "\r\n"
        try 
            let r = RunScript(text)
            r.[0].BaseObject :?&amp;gt; System.Collections.Hashtable
        with
        | :? ParameterBindingException as fault -&amp;gt;
            Console.WriteLine("{0}", fault.Message)
            Usage app
            reraise()&lt;/pre&gt;&lt;br /&gt;
&lt;p&gt;A simple example driver would be&lt;/p&gt;&lt;pre class="brush: f#"&gt;[&amp;lt;EntryPoint&amp;gt;]
    let main a =
        let arg = Value {Name = "ValueArg"; Required = false; Type = typeof&amp;lt;string&amp;gt;; Usage = "This is the value usage"}
        let arg2 = Value {Name = "NextArg"; Required = false; Type = typeof&amp;lt;int&amp;gt;; Usage = "This is the 2nd value usage"}
        let switch = Switch {Name = "SwitchArg"; Usage = "This is the switch usage" }
        let here = Assembly.GetExecutingAssembly().Location
        let h = (new FileInfo(here)).Name
        
        
        let app = { Name = h; Synopsis = "This is the synopsis" ;
                    Description = [| "this command does stuff"; "wonderful stuff" |] ;
                    Arguments = [| arg; arg2; switch |] }
        
        let hash = CommandLineParser.Parse app &amp;lt;| CommandLineParser.ExtractCommandLineArgumentString()
        hash.Keys |&amp;gt; Seq.cast 
        |&amp;gt; Seq.iter (fun k -&amp;gt; printfn "%A -&amp;gt; %A" k (hash.Item(k)))
        
        0&lt;/pre&gt;&lt;p&gt;There are some quirks -- Mandatory positional parameters and arbitrary nameless options don't mix : the current example program if given a command line "-Val hello -N 23 -S a b c" will yield&lt;/p&gt;&lt;br /&gt;
&lt;pre class="code"&gt;"$args" -&gt; [|"a"; "b"; "c"|]
"ValueArg" -&gt; "hello"
"SwitchArg" -&gt; True
"NextArg" -&gt; 23&lt;/pre&gt;&lt;br /&gt;
&lt;p&gt;Make &lt;code&gt;NextArg&lt;/code&gt; mandatory, and instead it goes&lt;/p&gt;&lt;pre class="code"&gt;A positional parameter cannot be found that accepts argument 'a'.

NAME
    ConsoleApplication1.exe

SYNOPSIS
    This is the synopsis

SYNTAX
    ConsoleApplication1.exe [[-ValueArg] &amp;lt;string&amp;gt;] [-NextArg] &amp;lt;int32&amp;gt; [-SwitchArg]...&lt;/pre&gt;&lt;p&gt;So, perhaps not industrial strength; but suited to gentle use when there's nothing else to hand.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-9024178992884859996?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/9024178992884859996/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=9024178992884859996' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/9024178992884859996'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/9024178992884859996'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/10/harnessing-powershell-command-line.html' title='Harnessing the PowerShell command line parser in .net'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-1458134177596666407</id><published>2011-10-09T20:56:00.001+01:00</published><updated>2011-10-09T20:56:28.339+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Links for 9-Oct</title><content type='html'>&lt;p&gt;&lt;a href="http://blogs.msdn.com/b/xmlteam/archive/2011/10/08/the-world-has-moved-on-have-you-xml-apis-you-should-avoid-using.aspx"&gt;The world has moved on, have you? Xml APIs you should avoid using. (Microsoft XML Team's WebLog)&lt;/a&gt; -- tl;dr = XmlText* classes should be retired in favour of objects minted by Xml*.Create() factories.&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.firstamong.com/5-awesome-free-jquery-plugins/"&gt;5 Awesome Free jQuery Plugins&lt;/a&gt; -- toast, sliders, tickers...&lt;/p&gt;&lt;br /&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-1458134177596666407?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/1458134177596666407/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=1458134177596666407' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/1458134177596666407'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/1458134177596666407'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/10/links-for-9-oct.html' title='Links for 9-Oct'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-321585711075246698</id><published>2011-10-09T10:29:00.002+01:00</published><updated>2011-10-09T10:32:22.531+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='PowerShell'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>What you see vs what you get : matching source and IL with Mono.Cecil and PowerShell</title><content type='html'>&lt;p&gt;&lt;a href="http://stevegilham.blogspot.com/2011/10/what-you-see-vs-what-you-get-matching.html"&gt;Following on from yesterday&lt;/a&gt;, looking at how Mono.Cecil pairs up debug information in the .pdb file with just the sequence points the IL -- which follows the approach taken by coverage tools such as &lt;a href="http://code.google.com/p/dot-net-coverage/"&gt;dot-net-coverage&lt;/a&gt;.  The code is much the same as before:&lt;/p&gt;&lt;pre class="brush: c#"&gt;&amp;lt;# 
.SYNOPSIS 
    This script inspects a method by introspection. 
    
.DESCRIPTION 
    This script uses Mono.Cecil introspection to tie instructions with source.
    It loads the referenced assemblies into the current AppDomain, so should be run in a
    separate process to your main script
        
.NOTES 
    File Name  : Inspect-MethodWithMonoCecil.ps1 
    Requires   : PowerShell Version 2.0 (3.0 or alternative launcher for .net 4/FxCop 10.0)
    
.EXAMPLE
 .\Inspect-MethodWithMonoCecil.ps1 -CecilPath ...\Mono.Cecil -AssemblyPath ...\Tinesware.Rules.dll 
         -ClassName Tinesware.Rules.SpellCheckRule 
         -MethodName VisitField | ? { $_.Context -ne "&amp;lt;None&amp;gt;" } | Out-GridView  
           
.PARAMETER CecilPath

The path to the directory where Mono.Cecil.*.dll have been installed

.PARAMETER AssemblyPath

The path to the assembly to inspect

.PARAMETER ClassName

The name of the class containg the Method.  Nested types are separated with + e.g.
  Project.Namespace.Type+NestedType

.PARAMETER MethodName

The name of the method to inspect
#&amp;gt; 
param ( 
    [Parameter(Mandatory = $true)] [string] $CecilPath,
    [Parameter(Mandatory = $true)] [string] $AssemblyPath,
    [Parameter(Mandatory = $true)] [string] $ClassName,
    [Parameter(Mandatory = $true)] [string] $MethodName,
    [switch] $Help)
    

if ($help -or (-not (Test-Path $CecilPath -PathType Container)) -or 
              (-not (Test-Path $AssemblyPath -PathType Leaf)) -or
              (-not (Test-Path $(Join-Path -Path $CecilPath -ChildPath 'Mono.Cecil.dll') -PathType Leaf)))
{
    Get-Help $MyInvocation.MyCommand.Definition
    return
}

Add-Type -Path $(Join-Path -Path $CecilPath -ChildPath 'Mono.Cecil.dll')
Add-Type -Path $(Join-Path -Path $CecilPath -ChildPath 'Mono.Cecil.Pdb.dll')

# load assembly with debug symbols
# We could reflect into $assembly to get the built-in symbol path, which is messy
# In most case it's safe to assume the files are co-located
$assembly = [Mono.Cecil.AssemblyDefinition]::ReadAssembly($AssemblyPath)
$symbolPath = [System.IO.Path]::ChangeExtension($assembly.MainModule.FullyQualifiedName, ".pdb")
$provider = New-Object Mono.Cecil.Pdb.PdbReaderProvider
$reader = $provider.GetSymbolReader($assembly.MainModule, $symbolPath)
$assembly.MainModule.ReadSymbols($reader)

# load the type
$splitnames = @($ClassName.Split('+'))

$types = @($assembly.Modules | % { $_.Types })
$type = $types | ? { $_.FullName -eq $splitnames[0] } | Select-Object -First 1

# handle nested types TODO
if (($splitnames.Length -gt 1) -and $type) {
  $splitnames[1..$($splitnames.Length-1)] | % {
    $name = $_
    if ($type) { $type = $type.NestedTypes | ? { $_.Name -eq $name } | Select-Object -First 1 }
    }
}

if (-not $type) {
  Write-Error "Class named '$ClassName' not found" -Category ObjectNotFound -TargetObject $ClassName
  return
}  

# load the method, being ready to resolve overloads
$methods = @($type.Methods  | ? { $_.Name -eq $MethodName })
if (-not $methods) {
  Write-Error "Method named '$MethodName' not found" -Category ObjectNotFound -TargetObject "$ClassName.$MethodName"
  return
}

# User choice adapted from http://blogs.technet.com/b/jamesone/archive/2009/06/24/how-to-get-user-input-more-nicely-in-powershell.aspx
Function Select-Item 
{
Param(   [String]$Caption="Please make a selection", 
         [String]$Message="Choices are presented below", 
         [String[]]$choiceList, 
         [int]$default=0) 
   $choicedesc = New-Object System.Collections.ObjectModel.Collection[System.Management.Automation.Host.ChoiceDescription] 
   $choiceList | foreach  { $choicedesc.Add((New-Object "System.Management.Automation.Host.ChoiceDescription" -ArgumentList $_))} 
   $Host.ui.PromptForChoice($caption, $message, $choicedesc, $default) 
}  

$index = 0
if ($methods.Length -gt 1) {
    $choices = @($methods | % { 
        $parameters = @($_.Parameters | % { "$($_.ParameterType) $($_.Name)" } )
        $parameters = $parameters -join ", "
        [string] "&amp;amp;$($index)$([char]8)$($_.Name)($Parameters)" 
        $index += 1
    })
    $index = Select-Item "Choose which overload to use" "The various overloads are:" $choices 0
}

# Overloads resolved, get the source
$method = $methods[$index]
$source = $method.Body.Instructions | % { $_.SequencePoint } | ? { $_ } | % { $_.Document } | ? { $_ } | % { $_.Url } | ? { $_ } | Select-Object -First 1
$sourceListing = Get-Content $source 

# Source context to code snippet
Function Extract-Snippet ([Mono.Cecil.Cil.SequencePoint] $context)
{
    if ( (-not $context.Document) -or (-not $context.Document.Url) -or (-not $context.EndLine) -or ($context.StartLine -ge $sourceListing.Length)) {
      "&amp;lt;None&amp;gt;"
    } else {
      $snippet = @($sourceListing[$($context.StartLine-1)..$($context.EndLine-1)])
      $snippet[0] = (" " * ($context.StartColumn-1)) + $snippet[0].SubString($context.StartColumn-1)
      $snippet[-1] = $snippet[-1].SubString(0, $context.EndColumn-1)
    }
}

## Report Instructions
$method.Body.Instructions | % {
    $sequencePoint = @($_.SequencePoint.StartLine, $_.SequencePoint.StartColumn, $_.SequencePoint.EndLine, $_.SequencePoint.EndColumn)
    if (-not $_.SequencePoint.EndLine) { $sequencePoint = "null" }
    if ($_.SequencePoint.StartLine -ge $sourceListing.Length) { $sequencePoint = "0xfeefee" }
    New-Object PSObject -Property @{   
        OpCode = $_.OpCode;
        Context = [System.String]::Join("`r`n", (Extract-Snippet $_.SequencePoint));
        Details = $_;
        SequencePoint = $sequencePoint
    }
}&lt;/pre&gt;&lt;p&gt;with the addition of the line/column data in SequencePoint ("null" if the values are not given, "0xfeefee" for the well-known compiler generated fake line number).&lt;/p&gt;&lt;br /&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-321585711075246698?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/321585711075246698/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=321585711075246698' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/321585711075246698'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/321585711075246698'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/10/what-you-see-vs-what-you-get-matching_09.html' title='What you see vs what you get : matching source and IL with Mono.Cecil and PowerShell'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-4386433256258593069</id><published>2011-10-08T21:24:00.003+01:00</published><updated>2011-10-09T10:49:43.559+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='PowerShell'/><category scheme='http://www.blogger.com/atom/ns#' term='IronPython'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Launching PowerShell 2.0 into .net4 via F# Interactive or IronPython 2.7</title><content type='html'>&lt;p&gt;With .net4 being 18 months old, and the Win8/PowerShell 3.0 being both in CTP and only for Win7+, it's a drag having to do things with .net 4 code when you really could do them faster and better in PowerShell.  So, why not bootstrap ourselves from out of the box PowerShell 2.0 into the .net 4 world with a little help from another scripting language which is .net 4 aware?  This saves messing about with environment activation variables, centralized registry settings or application config files, and takes advantage of the fact that PowerShell, like other scripting languages, can be hosted by another .net process.&lt;/p&gt;&lt;p&gt;The recipe is based around Bart de Smet's &lt;a href="http://community.bartdesmet.net/blogs/bart/archive/2010/07/06/hosting-windows-powershell-2-0-under-clr-4-0.aspx"&gt;Option 2 – Hosting Windows PowerShell yourself&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Via F# interactive:&lt;/p&gt;&lt;pre class="brush: c#"&gt;&amp;lt;# 
.SYNOPSIS 
    This script launches a basic PowerShell console running in .net v4
    
.DESCRIPTION 
    This script launches a basic PowerShell console running in .net v4, using F# interactive 
    for .net 4 as an intermediate platform
        
.NOTES 
    File Name  : Launch-PowerShellInNet4ViaFSharpInteractive.ps1
    Requires   : PowerShell Version 2.0
#&amp;gt; 

$programFiles = [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::ProgramFiles)

$fsharp4 = Join-Path -Path $programFiles -childPath 'Microsoft F#\v4.0\Fsi.exe'
$automation = Join-Path -Path $programFiles -childPath 'Reference Assemblies\Microsoft\WindowsPowerShell\v1.0\System.Management.Automation.dll'
$powershell = Join-Path -Path $programFiles -childPath 'Reference Assemblies\Microsoft\WindowsPowerShell\v1.0\Microsoft.PowerShell.ConsoleHost.dll'

$filename = [System.IO.Path]::GetRandomFileName()
$tempfile = Join-Path -path $env:temp -childPath "$filename"
$launcher = @"
open System
open System.Management.Automation.Runspaces
open Microsoft.PowerShell

let config = RunspaceConfiguration.Create()
ConsoleShell.Start(config,
                          "Windows PowerShell - Hosted on CLR v4\nCopyright (C) 2010 Microsoft Corporation. All rights reserved.",
                          String.Empty,
                          [| "-NoExit" ; "-Command" ; "[System.Console]::Title = \"PowerShell on .net \" + [System.Runtime.InteropServices.RuntimeEnvironment]::GetSystemVersion()" |]);;
"@
$source = $tempfile + ".fsx"

Set-Content -Path $source -Value $launcher

Write-Host "launching..."
$process = Start-Process -FilePath $fsharp4 -ArgumentList "`"-r:$automation`" `"-r:$powershell`" `"$source`"" -Wait
Remove-Item -Force $source&lt;/pre&gt;&lt;p&gt;Via IronPython 2.7 (augmented to take command line arguments via the -Options parameter):&lt;/p&gt;&lt;pre class="brush: c#"&gt;&amp;lt;# 
.SYNOPSIS 
    This script launches a basic PowerShell console running in .net v4
    
.DESCRIPTION 
    This script launches a basic PowerShell console running in .net v4, using IronPython 2.7 
    for .net 4 as an intermediate platform
        
.NOTES 
    File Name  : Launch-PowerShellInNet4ViaIronPython27.ps1
    Requires   : PowerShell Version 2.0
    
.PARAMETER Options
    Array of command line arguments passed to the inner PowerShell
#&amp;gt; 

param ( [string[]]$Options = @("-NoExit" , "-Command" , '[System.Console]::Title = "PowerShell on .net " + [System.Runtime.InteropServices.RuntimeEnvironment]::GetSystemVersion()') )


$programFiles = [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::ProgramFiles)

$ipy = Join-Path -Path $programFiles -childPath 'IronPython 2.7\ipy.exe'
$poshpath = Join-Path -Path $programFiles -childPath 'Reference Assemblies\Microsoft\WindowsPowerShell\v1.0'

$filename = [System.IO.Path]::GetRandomFileName()
$tempfile = Join-Path -path $env:temp -childPath "$filename"
$list = $Options -join "', '"
$launcher = @"
import clr
import sys

sys.path.append(r'$poshpath')
clr.AddReferenceToFile('System.Management.Automation.dll')
clr.AddReferenceToFile('Microsoft.PowerShell.ConsoleHost.dll')

from System import *
from System.Management.Automation.Runspaces import *
from Microsoft.PowerShell import *

config = RunspaceConfiguration.Create()
ConsoleShell.Start(config,
                   "Windows PowerShell - Hosted on CLR v4\nCopyright (C) 2010 Microsoft Corporation. All rights reserved.",
                   String.Empty,
                   Array[str]([ '$list' ]))
"@

$source = $tempfile + ".py"

Set-Content -Path $source -Value $launcher

Write-Host "launching..."
$process = Start-Process -FilePath $ipy -ArgumentList "`"$source`"" -Wait
Remove-Item -Force $source&lt;/pre&gt;&lt;p&gt;Since we can't pipe into &lt;code&gt;Start-Process&lt;/code&gt;, the inner script is written to a temporary file; and then the initial PowerShell process waits so that it can safely delete it.  If you would rather, it's reasonable to save the script to a constant &lt;code&gt;.fsx&lt;/code&gt; or &lt;code&gt;.py&lt;/code&gt;, replace &lt;code&gt;$source&lt;/code&gt; with the path to that file, then the &lt;code&gt;-Wait&lt;/code&gt; and &lt;code&gt;Remove-Item -Force $source&lt;/code&gt; can be removed.&lt;/p&gt;&lt;p&gt;Replacing the fixed command line arguments for the F# interactive launched PowerShell with ones input to the script as per IronPython follows the same pattern.  In either case, care has to be taken with getting your quotation marks right, and the IPy example is rather rough-and-ready.  Besides, doing appropriate quotation parsing and escaping would obscure the real point of the exercise.&lt;/p&gt;&lt;p&gt;&lt;b&gt;Note:&lt;/b&gt; Launching PowerShell directly from the .net4 command line launches PowerShell in the plain old .net 2 configuration; it is, alas, not sticky.&lt;/p&gt;&lt;b&gt;Note:&lt;/b&gt; These scripts are intended to give you a .net4 based interactive session; an alternative approach aimed at running individual commands can be found on &lt;a href="https://gist.github.com/882528"&gt;Jason Stangroome's GitHub&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/5569894-4386433256258593069?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/4386433256258593069/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=4386433256258593069' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/4386433256258593069'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/4386433256258593069'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/10/launching-powershell-20-into-net4-via-f.html' title='Launching PowerShell 2.0 into .net4 via F# Interactive or IronPython 2.7'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-9032221765302277271</id><published>2011-10-08T19:04:00.001+01:00</published><updated>2011-10-09T10:43:30.936+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='PowerShell'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>What you see vs what you get : matching source and IL with FxCop and PowerShell</title><content type='html'>&lt;p&gt;The mapping between the code you write and the object code that gets generated is not always a simple one; F# code in particular is heavily restructured in the compilation phase.  So when trying to perform code analysis to detect source level artefacts in the compiled code, some guide is useful to navigate between the two representations.&lt;/p&gt;&lt;p&gt;So, here is a PowerShell script to do just that, looking at the IL instructions or logical statements for a method and the corresponding source code as given via the .pdb debugging information -- PowerShell rather than F# interactive because the former has a much nicer user interaction model, especially when asking the user to resolve which overloaded method name was really intended.&lt;/p&gt;&lt;pre class="brush: c#"&gt;&amp;lt;# 
.SYNOPSIS 
    This script inspects a method by introspection. 
    
.DESCRIPTION 
    This script uses FxCop introspection to tie statements or instructions with source.
    It loads the referenced assemblies into the current AppDomain, so should be run in a
    separate process to your main script
        
.NOTES 
    File Name  : Inspect-Method.ps1 
    Requires   : PowerShell Version 2.0 (3.0 or alternative launcher for .net 4/FxCop 10.0)
    
.PARAMETER FxCopPath

The path to the directory where FxCop has been installed

.PARAMETER AssemblyPath

The path to the assembly to inspect

.PARAMETER ClassName

The name of the class containg the Method.  Nested types are separated with + e.g.
  Project.Namespace.Type+NestedType

.PARAMETER MethodName

The name of the method to inspect

.PARAMETER Instructions

By default, the output matches FxCop statements to source; with this switch, matches
IL instructions to the source.
#&amp;gt; 
param ( 
    [string] $FxCopPath,
    [Parameter(Mandatory = $true)] [string] $AssemblyPath,
    [Parameter(Mandatory = $true)] [string] $ClassName,
    [Parameter(Mandatory = $true)] [string] $MethodName,
    [switch] $Instructions,
    [switch] $Help)

$fxcopVersion = @{ "v2"='Microsoft FxCop 1.36'; "v4"='Microsoft FxCop 10.0' }

if (-not $FxCopPath) {
  $programFiles = [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::ProgramFiles)
  $dotNetVersion = [System.Runtime.InteropServices.RuntimeEnvironment]::GetSystemVersion().Split('.') | Select-Object -First 1
  $FxCopPath = Join-Path -Path $programFiles -ChildPath ($fxcopVersion[$dotNetVersion])
}

if ($help -or (-not (Test-Path $FxCopPath -PathType Container)) -or 
              (-not (Test-Path $AssemblyPath -PathType Leaf)) -or
              (-not (Test-Path $(Join-Path -Path $FxCopPath -ChildPath 'FxCopSdk.dll') -PathType Leaf)))
{
    Get-Help $MyInvocation.MyCommand.Definition
    return
}

# load assembly with debug symbols
Add-Type -Path $(Join-Path -Path $FxCopPath -ChildPath 'FxCopSdk.dll')
$assembly = [Microsoft.FxCop.Sdk.AssemblyNode]::GetAssembly($AssemblyPath, $null, $false, $true, $true, $false)

# load the type
$splitnames = @($ClassName.Split('+'))
$type = $assembly.Types | ? { $_.FullName -eq $splitnames[0] } | Select-Object -First 1

# handle nested types
if (($splitnames.Length -gt 1) -and $type) {
  $splitnames[1..$($splitnames.Length-1)] | % {
    $name = $_
    if ($type) { $type = $type.NestedTypes | ? { $_.Name.Name -eq $name } | Select-Object -First 1 }
    }
}

if (-not $type) {
  Write-Error "Class named '$ClassName' not found" -Category ObjectNotFound -TargetObject $ClassName
  return
}  

# load the method, being ready to resolve overloads
$methods = @($type.Members  | ? { ($_.Name.Name -eq $MethodName) -and ($_.NodeType -eq [Microsoft.FxCop.Sdk.NodeType]::Method) })
if (-not $methods) {
  Write-Error "Method named '$MethodName' not found" -Category ObjectNotFound -TargetObject "$ClassName.$MethodName"
  return
}

# User choice adapted from http://blogs.technet.com/b/jamesone/archive/2009/06/24/how-to-get-user-input-more-nicely-in-powershell.aspx
Function Select-Item 
{
Param(   [String]$Caption="Please make a selection", 
         [String]$Message="Choices are presented below", 
         [String[]]$choiceList, 
         [int]$default=0) 
   $choicedesc = New-Object System.Collections.ObjectModel.Collection[System.Management.Automation.Host.ChoiceDescription] 
   $choiceList | foreach  { $choicedesc.Add((New-Object "System.Management.Automation.Host.ChoiceDescription" -ArgumentList $_))} 
   $Host.ui.PromptForChoice($caption, $message, $choicedesc, $default) 
}  

$index = 0
if ($methods.Length -gt 1) {
    $choices = @($methods | % { 
        [string] "&amp;amp;$($index)$([char]8)$($_.Name.Name)($($_.Parameters))" 
        $index += 1
    })
    $index = Select-Item "Choose which overload to use" "The various overloads are:" $choices 0
}

# Overloads resolved, get the source
$method = $methods[$index]
$source = $method.Instructions | % { $_.SourceContext.FileName } | ? { $_ } | Select-Object -First 1
$sourceListing = Get-Content $source 

# Source context to code snippet
Function Extract-Snippet ([Microsoft.FxCop.Sdk.SourceContext] $context, [string] $prefix="")
{
    if ( (-not $context.FileName) -or (-not $context.EndLine) -or ($context.StartLine -ge $sourceListing.Length)) {
      "&amp;lt;None&amp;gt;"
    } else {
      $snippet = @($sourceListing[$($context.StartLine-1)..$($context.EndLine-1)])
      $snippet[0] = (" " * ($context.StartColumn-1)) + $snippet[0].SubString($context.StartColumn-1)
      $snippet[-1] = $snippet[-1].SubString(0, $context.EndColumn-1)
      $snippet | % { $prefix + $_ }
    }
}

## Report Instructions if requested, then exit
if ($Instructions) {
    $method.Instructions | % {
        New-Object PSObject -Property @{   
            OpCode = $_.OpCode;
            Context = [System.String]::Join("`r`n", (Extract-Snippet $_.SourceContext));
            Details = $_
        }
    }
    return
}

# Expand a StatementCollection recursively, indicating block structure with levels of "&amp;gt;")
Function Expand-Block([Microsoft.FxCop.Sdk.StatementCollection] $block, [string] $prefix="") {
  $block | % {
    New-Object PSObject -Property @{   
        NodeType = $_.NodeType;
        Context = [System.String]::Join("`r`n", (Extract-Snippet $_.SourceContext $prefix));
        Details = $_
        }
    ## recurse into blocks
    if ($_.NodeType -eq [Microsoft.FxCop.Sdk.NodeType]::Block) { Expand-Block $_.Statements ($prefix+"&amp;gt;") }
    }
}

## Report Statements
Expand-Block $method.Body.Statements&lt;/pre&gt;&lt;p&gt;As the script writes rich objects in the successful output to pipeline, it can be used as a starting point for further analysis; or the output can be piped into e.g. &lt;code&gt;Out-GridView&lt;/code&gt; for inspection.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-9032221765302277271?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/9032221765302277271/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=9032221765302277271' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/9032221765302277271'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/9032221765302277271'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/10/what-you-see-vs-what-you-get-matching.html' title='What you see vs what you get : matching source and IL with FxCop and PowerShell'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-9136295504248101110</id><published>2011-10-02T11:39:00.001+01:00</published><updated>2011-10-02T11:39:17.581+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='weather'/><category scheme='http://www.blogger.com/atom/ns#' term='cycling'/><title type='text'>Summer at last</title><content type='html'>&lt;p&gt;So, we get the hottest days of the year at the start of October : it's like summer was put on back to front, going from warm, to April showers, to autumn, to high summer.&lt;/p&gt;&lt;p&gt;After clocking up to ~2705 miles in Q3 -- 800 miles in the quarter -- I took a ~40 mile spin on the bike yesterday, with lunch at the Carpenter's Arms in Gt. Wilbraham, then carrying on out past Six Mile Bottom, West Wratting, Balsham and back.  With the lack of wind, and low humidity, it was a beguilingly easy ride (apart from the long grinds up out of Six Mile Bottom towards Brinkley; and from West Wratting to Balsham).  So easy, that it wasn't obvious quite how much I was sweating, and thus how much I needed to rehydrate, until I finished a 75cl bottle of Lucozade Sport in pretty much one gulp.&lt;/p&gt;&lt;p&gt;In the evening, it was still so warm that we sat out in the garden until well after sunset, something that we never had weather for over the nominal summer.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-9136295504248101110?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/9136295504248101110/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=9136295504248101110' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/9136295504248101110'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/9136295504248101110'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/10/summer-at-last.html' title='Summer at last'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-5837564161966070751</id><published>2011-09-22T22:05:00.003+01:00</published><updated>2011-09-22T22:09:08.338+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='book'/><title type='text'>Recent reading</title><content type='html'>&lt;h4&gt;&lt;i&gt;Kethani&lt;/i&gt; by Eric Brown&lt;/h4&gt;&lt;p&gt;Aliens send artefacts to Earth to enslave the human race, as told through the perspective of the inhabitants of a Yorkshire village with an unusually high rate of cancer and suicide.  Nobody seems to notice what the aliens are doing, not even the author.&lt;/p&gt;&lt;p&gt;Cosy British SF as it is done at the start of the new century.&lt;/p&gt;&lt;h4&gt;&lt;i&gt;Yellow Blue Tibia&lt;/i&gt; by Adam Roberts&lt;/h4&gt;&lt;p&gt;Cold War nostalgia in a tale of an SF writer given an unusual assignment by Stalin, and then being told to forget everything, which works for the next 30-some years.  Spends a long time setting up the mood, then infodumps a not-quite-resolution.&lt;/p&gt;&lt;h4&gt;&lt;i&gt;Rocket Girls&lt;/i&gt; and &lt;i&gt;Rocket Girls: The Last Planet&lt;/i&gt; by Housuke Norjiri&lt;/h4&gt;&lt;p&gt;The two light novels (each slim enough to finish during one bathtime) that were the original source of &lt;a href="http://stevegilham.blogspot.com/2007/07/anime-rocket-girls.html"&gt;2007's delightful anime series&lt;/a&gt;, now available in English.  The anime was a very faithful adaptation of the source material -- just omitting the by then very dated visit to &lt;i&gt;Mir&lt;/i&gt; at the end of the first book, and segueing that mission into the one at the start of the second.&lt;/p&gt;&lt;h4&gt;&lt;i&gt;Ouroboros Wave&lt;/i&gt; by Jyouji Hayashi&lt;/h4&gt;&lt;p&gt;A series of almost puzzle-driven hard-SF shorts scattered across several decades in a fairly standard Earth vs space colonists setting -- a very modern execution of something that was at one time almost the backbone of the genre.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-5837564161966070751?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/5837564161966070751/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=5837564161966070751' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/5837564161966070751'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/5837564161966070751'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/09/recent-reading.html' title='Recent reading'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-1631735090968420486</id><published>2011-09-02T22:25:00.001+01:00</published><updated>2011-09-02T22:25:35.557+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='weather'/><title type='text'>A summer evening at last</title><content type='html'>&lt;p&gt;After work, along with a couple of colleagues, I cycled to the Toft Beer Festival, where we sat at the table outside in the warm evening sunshine and enjoyed the beer, until it came sunset, and time to be heading home.&lt;/p&gt;&lt;p&gt;For the first time this year, the warmth lingered as the light went.  It was pleasant cycling in shorts and T-shirt; and even just a few minutes ago, going out to drop tea-bags and apple cores into the compost bin, wearing nothing but a towel around my waist, the paving was warm underfoot, and the air mild long after sunset.&lt;/p&gt;&lt;p&gt;I had in the past come to expect the light to be going before the warmth through July, but not so in recent years.  Now that is a most unusual thing to happen until an Indian Summer day like today.&lt;/p&gt;&lt;br /&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-1631735090968420486?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/1631735090968420486/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=1631735090968420486' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/1631735090968420486'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/1631735090968420486'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/09/summer-evening-at-last.html' title='A summer evening at last'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-7403629211922518666</id><published>2011-08-28T22:49:00.006+01:00</published><updated>2011-08-28T23:24:29.446+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Film'/><title type='text'>Cambridge Film Festival</title><content type='html'>&lt;p&gt;I pretty much &lt;a href="http://stevegilham.blogspot.com/2003/07/cambridge-film-festival-log-part-i.html"&gt;started this blog as as a home for "My Cambridge Film Festival film reviews"&lt;/a&gt;, however much my blogging has mutated over the years. And this weekend, &lt;a href="http://issuu.com/camfilmtrust/docs/cff2011_brochure?mode=embed&amp;layout=http%3A%2F%2Fskin.issuu.com%2Fv%2Fcolor%2Flayout.xml&amp;backgroundColor=A4112B&amp;showFlipBtn=true"&gt;the programme&lt;/a&gt; for &lt;a href="http://www.cambridgefilmfestival.org.uk/news/2011/08/27/cff2011-programme-online/"&gt;this year's CFF&lt;/a&gt; is out -- and &lt;a href="http://stevegilham.blogspot.com/2010/08/cambridge-film-festival.html"&gt;even more than last year&lt;/a&gt;, I'm feeling underwhelmed by the torrent of gritty and edgy features, right-on documentaries, and the usual run of "there's adultery in it, so it must be artistic".  It's like it was all set out with a ground rule of "No fun allowed, SRS BZNS ONLY".  Oh, and they're exhuming and showing the totally abysmal &lt;span style="font-style:italic;"&gt;Silent Running&lt;/span&gt; (down there near the nadir of my list of the worst movies I've watched). Twice.&lt;/p&gt;
&lt;p&gt;I suppose I could go watch &lt;span style="font-style:italic;"&gt;The Seventh Seal&lt;/span&gt; and &lt;span style="font-style:italic;"&gt;The Day the Earth Caught Fire&lt;/span&gt; again; but apart from being in two minds about this locally produced film, &lt;a href="http://dimensionsthemovie.com/the-movie/"&gt;&lt;span style="font-style:italic;"&gt;Dimensions&lt;/span&gt;&lt;/a&gt; (which could be another &lt;a href="http://stevegilham.blogspot.com/2008/09/film-time-crimes-los-cronocrmenes.html"&gt;&lt;span style="font-style:italic;"&gt;Time Crimes&lt;/span&gt;&lt;/a&gt;, but could equally well be artsy tosh with a few bits of stage dressing like non-genre writers generate when thinking they're doing SF), the new material singularly failed to catch my eye.&lt;/p&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-7403629211922518666?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/7403629211922518666/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=7403629211922518666' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/7403629211922518666'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/7403629211922518666'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/08/cambridge-film-festival.html' title='Cambridge Film Festival'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-3976789923838678487</id><published>2011-08-25T18:48:00.004+01:00</published><updated>2011-08-28T23:36:25.299+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='cycling'/><title type='text'>Late summer cycling break</title><content type='html'>&lt;p&gt;Same sort of gig as &lt;a href="http://stevegilham.blogspot.com/2010/08/cycling-holiday.html"&gt;last year&lt;/a&gt;, only with different weather.&lt;/p&gt;

&lt;iframe width="425" height="350" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="http://maps.google.co.uk/maps/ms?msa=0&amp;amp;msid=214190258906006153051.0004ab57a7f89d57201b6&amp;amp;ie=UTF8&amp;amp;vpsrc=6&amp;amp;ll=52.88902,0.705872&amp;amp;spn=0.290007,0.583649&amp;amp;z=10&amp;amp;output=embed"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;small&gt;View &lt;a href="http://maps.google.co.uk/maps/ms?msa=0&amp;amp;msid=214190258906006153051.0004ab57a7f89d57201b6&amp;amp;ie=UTF8&amp;amp;vpsrc=6&amp;amp;ll=52.88902,0.705872&amp;amp;spn=0.290007,0.583649&amp;amp;z=10&amp;amp;source=embed" style="color:#0000FF;text-align:left"&gt;August 2011&lt;/a&gt; in a larger map&lt;/small&gt;

&lt;p&gt;This time I arrived in Great Bircham about 10:30 on a beautiful summer's day, and headed out on the long run I'd meant to do last year, without the interruption by squall lines.&lt;/p&gt;
&lt;p&gt;With the later start, I reached Little Walsingham after the first course of packed lunch (stopping for a pint at the Black Lion), and then kept going east as far as Binham (another pint at the Chequers Inn -- which, despite the name, does not seem to offer accommodation, but would have made a good base for another time), then looping back.&lt;/p&gt;
&lt;p&gt;Second course of lunch -- cheesy rolls and a beefsteak tomato from the greenhouse -- at 15:00, partway along the coastal cyclepath, before heading for the first time through the grounds of Holkham Hall from the north.  I'd steered clear of it, having previously &lt;a href="http://stevegilham.blogspot.com/2006/07/to-sea-and-back.html"&gt;taken other paths from the south&lt;/a&gt; which didn't make good cycling.&lt;/p&gt;
&lt;p&gt;This side, the paths were properly made, easy going up and over the rolling terrain, then rolling downhill most of the way to Burnham Market; then the grind up the Ringstead road was where my legs started to tire, but still back to base not long after 17:00, having done about 48 miles.&lt;/p&gt;
&lt;p&gt;Tuesday was wet, so read, did internet and generally rested up, with a little stroll around the local area late afternoon.&lt;/p&gt;
&lt;p&gt;Wednesday was dry, if cool and generally cloudy, so another clockwise loop through the area (with one misstep when the path I'd meant to take was less obvious on the ground than the one I didn't), through lanes I'd mainly not taken before, with the goal of lunch at the Gin Trap Inn at Ringstead.&lt;/p&gt;
&lt;p&gt;After their substantial club sandwich and chocolate mousse, I didn't want to take the rather punishing direct route to Sedgeford, but instead detoured out to Heacham, before finally ambling back (with a number of pauses where a full stomach made it more comfortable to get off and push rather than grind an uphill).&lt;/p&gt;
&lt;p&gt;So that was about 28 miles; but between the end-of-day fade on Monday, and the push while digesting, my logged average speed went down from a steady 11 mph as it had been all summer, to only 10.8 :(&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-3976789923838678487?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/3976789923838678487/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=3976789923838678487' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/3976789923838678487'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/3976789923838678487'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/08/late-summer-cycling-break.html' title='Late summer cycling break'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-1594709854755087553</id><published>2011-08-15T22:35:00.004+01:00</published><updated>2011-08-15T22:53:28.455+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='PowerShell'/><title type='text'>Powershell -- higher order functions with arguments that take arguments</title><content type='html'>&lt;p&gt;Dead simple example &lt;a href="http://www.dougfinke.com/blog/index.php/2010/12/11/does-powershell-support-high-order-functions/"&gt;based on this&lt;/a&gt;:&lt;/p&gt;
&lt;pre class="brush: c#"&gt;# Create a function that accepts a function that takes arguments
Function Test ([scriptblock]$code, $name) {

    &amp; $code "$name+$name" | % {
        New-Object PSObject -Property @{
            Name = $name
            Prop = "**$_**"
        }
    }
}    

# Create a function to call
Function  Set-Lower ([string]$x) {
  $x.ToLowerInvariant()
}   

# Create a suitable script block to wrap the function and take a parameter
$b = [scriptblock] { param([string]$Target = "PARAM") Set-Lower $Target }            

# Invoke the Test function passing it a scriptblock and argument
Test $b Junku&lt;/pre&gt;
&lt;p&gt;which results in&lt;/p&gt;
&lt;pre class="code"&gt;Name                                                  Prop                                                 
----                                                  ----                                                 
Junku                                                 **junku+junku**                                      &lt;/pre&gt;
&lt;p&gt;Also -- reminder to self : &lt;a href="http://showui.codeplex.com/"&gt;ShowUI -- WPF in PowerShell&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/5569894-1594709854755087553?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/1594709854755087553/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=1594709854755087553' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/1594709854755087553'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/1594709854755087553'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/08/powershell-higher-order-functions-with.html' title='Powershell -- higher order functions with arguments that take arguments'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-6919898531801691763</id><published>2011-08-15T21:36:00.002+01:00</published><updated>2011-08-15T21:45:30.817+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Mileage'/><category scheme='http://www.blogger.com/atom/ns#' term='cycling'/><category scheme='http://www.blogger.com/atom/ns#' term='garden'/><title type='text'>Trivia</title><content type='html'>&lt;p&gt;At end of day 11-Aug the car was reading 3474 miles; at the hour that I picked it up last year, 3485.  Subtracting the 14 that were &lt;a href="http://stevegilham.blogspot.com/2010/08/shiny-new-toy.html"&gt;on the clock when I picked it up&lt;/a&gt;, 3460 or 3471 miles -- a lot less than in previous years.&lt;/p&gt;
&lt;p&gt;Sunday evening, the bike was showing 2280 miles; 373 miles in the first 6 weeks of the new year, which is in keeping with &lt;a href="http://stevegilham.blogspot.com/2010/10/season-in-saddle.html"&gt;last year's rate&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The new guided busway cyclepath is a great new way into the town centre avoiding traffic, even though I have to do a mile and a bit on bridlepath or detour twice that distance to get onto the cyclepath feeding to it.  If only there were paved spurs to the neighbouring villages.  And even though the last hundred yards or so at the station end aren't finished yet.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;In the garden, the plums have been in full flood for a couple of weeks -- much earlier than the usual Bank Holiday peak I usually plan for; indeed everything has been early, ever since the early onset of last winter.&lt;/p&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-6919898531801691763?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/6919898531801691763/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=6919898531801691763' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/6919898531801691763'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/6919898531801691763'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/08/trivia.html' title='Trivia'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-3280365942705895307</id><published>2011-08-05T23:19:00.006+01:00</published><updated>2011-08-27T18:23:38.227+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='PowerShell'/><title type='text'>What do you get when you iterate null?</title><content type='html'>&lt;p&gt;In most cases, an exception; but in PowerShell you get &lt;code&gt;$null&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Suppose I have folders with 0, 1 and more than 1 file in (called &lt;code&gt;empty&lt;/code&gt;, &lt;code&gt;full&lt;/code&gt; and &lt;code&gt;two&lt;/code&gt;, say) and I want to enumerate the names of the files contained in each, with some general purpose code.&lt;/p&gt;
&lt;p&gt;That should be no problem --&lt;/p&gt;
&lt;pre class="brush: c#"&gt;function Get-FileNames($dir) {
  $files = Get-ChildItem $dir
  foreach ($file in $files) {
    Split-Path -Leaf $file
  }
}&lt;/pre&gt;
&lt;p&gt;and let's write a little reporting function&lt;/p&gt;
&lt;pre class="brush: c#"&gt;function Get-Report($arg) {
  $arg
  $arg.GetType().FullName
  $arg.Length
  $arg | % { Write-Host "++$_++" }
  Write-Host "-----"
}&lt;/pre&gt;
&lt;p&gt;When we report on the folders in descending order of contained files we get&lt;/p&gt;
&lt;pre class="code"&gt;test (2).txt
test.txt
System.Object[]
2
++test (2).txt++
++test.txt++
-----
test.txt
System.String
8
++test.txt++
-----
&lt;span style="color:red"&gt;Split-Path : Cannot bind argument to parameter 'Path' because it is null.
At C:\Users\Steve\Documents\scratch\Untitled1.ps1:6 char:15
+     Split-Path &lt;&lt;&lt;&lt;  -Leaf $file
    + CategoryInfo          : InvalidData: (:) [Split-Path], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.Split 
   PathCommand
 
You cannot call a method on a null-valued expression.
At C:\Users\Steve\Documents\scratch\Untitled1.ps1:12 char:15
+   $arg.GetType &lt;&lt;&lt;&lt; ().FullName
    + CategoryInfo          : InvalidOperation: (GetType:String) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull&lt;/span&gt;
 
++++&lt;/pre&gt;
&lt;p&gt;Not happy.&lt;/p&gt;
&lt;p&gt;The first error is what you get when you work on the &lt;code&gt;foreach &lt;/code&gt;over &lt;code&gt;$null&lt;/code&gt;, yielding &lt;code&gt;$null&lt;/code&gt;; and calling code has to special-case the single item return -- getting the .Length property of the return is not the number of files!  For that replace &lt;code&gt;$arg.Length&lt;/code&gt; by &lt;code&gt;$arg | Measure-Object | % {write-host $_.Count}&lt;/code&gt; .&lt;/p&gt;
&lt;p&gt;So try this&lt;/p&gt;
&lt;pre class="brush: c#"&gt;function Get-Array($list)
{
  if (-not $list) {
    ,(@())
  } else {
    if ($list -is [array]) {
      $list
    } else {
      ,(,$list)
    }
  }
}

function Get-FileNamesX($dir)
{
  $files = Get-Array(Get-ChildItem $dir)
  foreach ($file in $files)
  {
    Split-Path -Leaf $file
  }
}

function Get-FileNames2($dir)
{
  Get-Array(Get-FileNamesX($dir))
}&lt;/pre&gt;
&lt;p&gt;where we fight PowerShell's fragmentation of sequences all the way.  Now we get what we expected&lt;/p&gt;
&lt;pre class="code"&gt;test (2).txt
test.txt
System.Object[]
2
++test (2).txt++
++test.txt++
-----
test.txt
System.Object[]
1
++test.txt++
-----
System.Object[]
0
-----&lt;/pre&gt;
&lt;hr&gt;
&lt;p&gt;Oh, yeah -- the more PowerShell way of doing all this:&lt;/p&gt;
&lt;pre class="brush: c#"&gt;function Get-FileNames($dir)
{
  Get-ChildItem $dir | % {
    Split-Path -Leaf $_
  }
}

function Get-Report {
  PARAM ($InputObject) 
  END {
  $arg = @($InputObject + $Input)
  $arg
  $arg.GetType().FullName
  $arg.Length
  $arg | Measure-Object | % {write-host "Measure = $($_.Count)"}
  $arg | % { Write-Host "++$_++" }
  Write-Host "-----"
 }
}

Get-FileNames(".\two") | Get-Report
Get-FileNames(".\full") | Get-Report
Get-FileNames(".\empty") | Get-Report&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-3280365942705895307?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/3280365942705895307/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=3280365942705895307' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/3280365942705895307'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/3280365942705895307'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/08/what-do-you-get-when-you-iterate-null.html' title='What do you get when you iterate null?'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-8838211993160851237</id><published>2011-07-31T23:40:00.004+01:00</published><updated>2011-08-01T07:45:31.462+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scala'/><category scheme='http://www.blogger.com/atom/ns#' term='functional programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Links for July</title><content type='html'>&lt;p&gt;&lt;a href="http://research.microsoft.com/en-us/projects/fstar/"&gt;Microsoft Research's new F* language&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://nodejs.org/#download"&gt;Node.js 0.5.2&lt;/a&gt; unstable branch now includes Windows&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.scala-lang.org/node/10299"&gt;Scala.net reloaded&lt;/a&gt; -- still need to try it out with code that broke the old version.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-8838211993160851237?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/8838211993160851237/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=8838211993160851237' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/8838211993160851237'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/8838211993160851237'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/07/links-for-july.html' title='Links for July'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-6700474398048025673</id><published>2011-07-31T23:33:00.003+01:00</published><updated>2011-08-11T13:44:52.075+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tourism'/><category scheme='http://www.blogger.com/atom/ns#' term='Film'/><category scheme='http://www.blogger.com/atom/ns#' term='weather'/><category scheme='http://www.blogger.com/atom/ns#' term='family'/><category scheme='http://www.blogger.com/atom/ns#' term='astronomy'/><category scheme='http://www.blogger.com/atom/ns#' term='food'/><title type='text'>July wrap-up</title><content type='html'>&lt;p&gt;Well, it's been a busy month, hence quiet on the blogging front, starting out with:&lt;/p&gt;
&lt;h4&gt;A summer holiday&lt;/h4&gt;
&lt;p&gt;While Karen was in hospital last year, I noticed a flyer for &lt;a href="http://www.vitalise.org.uk/"&gt;Vitalise&lt;/a&gt; respite care holidays in amongst various papers, and thought "Let's do this next summer."&lt;/p&gt;
&lt;p&gt;So we did.&lt;/p&gt;
&lt;p&gt;We chose the first week of July at the &lt;a href="http://www.vitalise.org.uk/centre_breaks/our_centres/netley_waterside_house_southampton/"&gt;Netley Waterside&lt;/a&gt; centre as a part of the country neither of us had been to for many years, went through all the paperwork about care requirements and doctor's reports, and crossed our fingers about how well we would get on.&lt;/p&gt;
&lt;p&gt;In the end, it was wonderful.&lt;/p&gt;
&lt;p&gt;It started off a bit fraught, though; as on the outward journey on a too hot and bright day for comfortable travel, as we were going past Tring, I realised that amongst the things I'd forgotten to pack was my wallet.  So I just unloaded Karen and all the other bits, and turned around, to drive down again the next day with all the stuff we've forgotten (doing in two days way more miles than I drive in the average month).&lt;/p&gt;
&lt;p&gt;Going back home in the heat was not much fun, especially when combined with stumbling into special traffic arrangements for the Henley Regatta just as everyone was heading home for the evening.  But at home, I was cheered to get a phone call from Karen, who was enjoying being able to choose her own bed time.&lt;/p&gt;
&lt;p&gt;Second time around I was actually able to find a parking space (as holidays run Saturday to Saturday, the first time there were arrivals and departures jostling for space), freshen up and join the Sunday chill-out.&lt;/p&gt;
&lt;p&gt;The centre is really on the water's edge&lt;/p&gt;
&lt;div class="center"&gt;&lt;a href="http://www.flickr.com/photos/stevegilham/5919216552/" title="View from the window by Steve Gilham, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6126/5919216552_68ab8564c9.jpg" width="500" height="375" alt="View from the window"&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;with the gardens sloping down to the path that runs along the sea wall, though the path is only wheelchair accessible in one direction.&lt;/p&gt;
&lt;p&gt;Monday, we opted out of the excursion to the Sea Life Center, and after I'd scouted out the area, we went along to the local (the Prince Consort) and took advantage of the ramp up to the beer garden at back. to just sit, enjoy a pint and the summer weather, the two of us on holiday together for the first time in years.&lt;/p&gt;
&lt;div class="center"&gt;&lt;a href="http://www.flickr.com/photos/stevegilham/5918799117/" title="Free flying by Steve Gilham, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6148/5918799117_1b05d4d305.jpg" width="500" height="375" alt="Free flying"&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;But that was the end of the good weather; cooler, windier and wet was the order of the day for the rest of the week.  Tuesday's expedition to the &lt;a href="http://www.hawk-conservancy.org/index.asp"&gt;Hawk Conservancy Trust&lt;/a&gt; at Andover was dampened by the rain arriving as we did, and continuing at varying strength until we left; but despite the weather, the display was spectacular, with sometimes a dozen birds in the air.&lt;/p&gt;
&lt;div class="center"&gt;&lt;a href="http://www.flickr.com/photos/stevegilham/5918865887/" title="Lemur by Steve Gilham, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6140/5918865887_593484d120.jpg" width="500" height="375" alt="Lemur"&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;We did manage to miss the rain for Wednesday's visit to &lt;a href="http://www.marwell.org.uk/"&gt;Marwell Zoo&lt;/a&gt;, which we only manage to see less than half of; Thursday started wet, so the planned trip to Winchester became a shopping center trip, which we opted out of; and the Friday was an official at-leisure day where we took the opportunity to slope off to the pub (using the industrial strength ramp for the front step) for lunch.&lt;/p&gt;
&lt;p&gt;And so, home.&lt;/p&gt;
&lt;p&gt;For the holiday, there was the inevitable fact of separate hospital beds for sleeping; the food all home cooked, if unadventurous, with a very early 18:00 dinner time, with breakfast being as much a main meal as that in the evening.&lt;/p&gt;
&lt;p&gt;Karen really enjoyed the comparative freedom to choose a bed time -- including at well gone midnight on Thursday, when it was party night.&lt;/p&gt;
&lt;p&gt;And for me -- well, when I helped Karen with chasing the last morsels of a meal around a plate, I was told off, that one of the volunteers should do that while I enjoyed a break!&lt;/p&gt;
&lt;p&gt;The volunteers are a key part of the operation -- a cheerful, enthusiastic and cosmopolitan crowd from all over the globe, working for bed and board, to just take over much of the step-and-fetch level care, from simply getting a cup of coffee, to pushing manual chairs while accompanying otherwise unaccompanied guests.&lt;/p&gt;
&lt;p&gt;So, we'll be back, though next time, assuming I don't have to do the trip 3 times in quick succession, we may opt out of scheduled trips and do our own at our own pace (without being caught up in the half hour or more loading and unloading process).&lt;/p&gt;
&lt;p&gt;Trivia -- at just before 10pm on Monday the 4th, I observed a 60-hour old nail-paring crescent moon setting over the port, the sun having set 40 minutes before.&lt;/p&gt;
&lt;h4&gt;30 years on&lt;/h4&gt;
&lt;p&gt;And today has been out Pearl wedding.  Doesn't time fly!&lt;/p&gt;
&lt;p&gt;Logistics being what they are, celebration was lunch, at the recently refurbished &lt;a href="http://www.carpentersarmsgastropub.co.uk/"&gt;Carpenters Arms&lt;/a&gt; at Gt. Wilbraham.  Being a Sunday, that meant roast, but by being so standard, allowed a like-for-like comparison : big chunks of roast, huge selection of veg, and the best Yorkshire pudding I've eaten in years.&lt;/p&gt;

&lt;h4&gt;Film &amp;#8212; &lt;cite&gt;Detective Dee and the Mystery of the Phantom Flame&lt;/cite&gt;&lt;/h4&gt;
&lt;p&gt;I this up picked up now it's out here on DVD, having heard of it some months ago.&lt;/p&gt;

&lt;p&gt;The film is pure hokum wire-fu, and about as faithful to any of the source material as most movies (about on the level of the wartime Sherlock Holmes vs. Nazi spy films) but entertaining enough for an evening's viewing.&lt;/p&gt;

&lt;p&gt;While van Gulik is in the writing credits according to imdb, I saw nothing of what he did by way of exhuming Chinese detective stories (except possibly the fact of popularising Judge Dee in the west) made it into the movie.&lt;/p&gt;
&lt;h4&gt;Film &amp;#8212; &lt;cite&gt;Arietty&lt;/cite&gt;&lt;/h4&gt;
&lt;p&gt;Seeing this after lunch today, we caught the UK theatrical dub (separate UK production team mentioned in the end credits, and some English regional accents -- most noticeably for the rat catchers -- amongst the cast).&lt;/p&gt;

&lt;p&gt;It's been a long long time since I read the original (I have slightly more memories of Adrift and Aloft than of Afield), but the adaptation felt right, though one wonders if it wasn't the speech about the Borrowers dying out that attracted Miyazaki, something to slide in as a soapboxing scene.&lt;/p&gt;

&lt;p&gt;Overall, the best thing out of Ghibli since The Cat Returns.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-6700474398048025673?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/6700474398048025673/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=6700474398048025673' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/6700474398048025673'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/6700474398048025673'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/07/july-wrap-up.html' title='July wrap-up'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://farm7.static.flickr.com/6126/5919216552_68ab8564c9_t.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-483955306824506764</id><published>2011-07-25T22:45:00.002+01:00</published><updated>2011-07-25T22:55:37.004+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='PowerShell'/><title type='text'>PowerShell strings, ints and comparisons</title><content type='html'>&lt;p&gt;I hit this doing a little bit of playing around over the weekend, porting some old BASIC progams into PowerShell.&lt;/p&gt;
&lt;p&gt;Most of the time treating strings of digits as numbers "just works":&lt;/p&gt;
&lt;pre class="brush: c#"&gt;&amp;gt;$line = "5, 22.5"
&amp;gt;$bits = $line.split(",")
&amp;gt;$bits
5
 22.5
&amp;gt;$ratio = $bits[1]/$bits[0]
&amp;gt;$ratio
4.5
&amp;gt;write-host ($bits[0] -eq 5)
True&lt;/pre&gt;
&lt;p&gt;but at times it all goes horribly wrong&lt;/p&gt;
&lt;pre class="brush: c#"&gt;&amp;gt;write-host ($bits[0] -gt 30)
True
&amp;gt;write-host ($bits[0] -gt 60)
False
&amp;gt;write-host ($bits[0] -gt 50)
False
&amp;gt;write-host ($bits[0] -gt 49)
True
&amp;gt;write-host ($bits[0] -eq 50)
False&lt;/pre&gt;
&lt;p&gt;with the ASCII value of '5' being 53, we see that these comparisons are being done lexically.&lt;/p&gt;
&lt;p&gt;What is needed in these circumstances is an explicit coercion some place down the line; like:&lt;/p&gt;
&lt;pre class="brush: c#"&gt;&gt;write-host ([int]$bits[0] -gt 45)
False&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-483955306824506764?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/483955306824506764/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=483955306824506764' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/483955306824506764'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/483955306824506764'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/07/powershell-strings-ints-and-comparisons.html' title='PowerShell strings, ints and comparisons'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-3153485182187112589</id><published>2011-06-30T22:21:00.004+01:00</published><updated>2011-06-30T22:44:28.650+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='PowerShell'/><title type='text'>Select-Xml and nested XPath evaluations</title><content type='html'>&lt;p&gt;&lt;a href="http://stevegilham.blogspot.com/2011/06/more-powershell-xml-and-xpath-select.html"&gt;Recall that&lt;/a&gt;&lt;/p&gt;
&lt;pre class="brush: c#"&gt;&amp;gt;$s = Select-Xml -Xml $x -XPath '//Rule'
&amp;gt;$s | foreach-object { $_.Node ; Select-Xml $_.Node -XPath 'descendant::BooleanProperty' |  foreach-object { $_.Node } }&lt;/pre&gt;
&lt;p&gt;worked but&lt;/p&gt;
&lt;pre class="brush: c#"&gt;&amp;gt;$s = Select-Xml -Xml $x -XPath '//Rule'
&amp;gt;$s | foreach-object { $_.Node ; Select-Xml $_ -XPath 'descendant::BooleanProperty' |  foreach-object { $_.Node } }&lt;/pre&gt;
&lt;p&gt;failed with error messages showing that the current iterated value has an XML string representation.&lt;/p&gt;
&lt;p&gt;An alternative fix is to explicitly cast the subject of the inner XPath selection to XML, thus&lt;/p&gt;
&lt;pre class="brush: c#"&gt;&amp;gt;$s = Select-Xml -Xml $x -XPath '//Rule'
&amp;gt;&gt;$s | foreach-object { $_.Node ; Select-Xml ([xml] $_) -XPath 'descendant::BooleanProperty' |  foreach-object { $_.Node } }&lt;/pre&gt;
&lt;p&gt;though surprisingly&lt;/p&gt;
&lt;pre class="brush: c#"&gt;&amp;gt;$s = Select-Xml -Xml $x -XPath '//Rule'
&amp;gt;&gt;$s | foreach-object { [xml] $_ ; Select-Xml ([xml] $_) -XPath 'descendant::BooleanProperty' |  foreach-object { $_.Node } }&lt;/pre&gt;
&lt;p&gt;fails to yield any results from the inner search; and I've not so far determined why.  Replacing the final &lt;code&gt;$_.Node&lt;/code&gt; by &lt;code&gt;[xml] $_&lt;/code&gt; of course fails because &lt;code&gt;False&lt;/code&gt; is not an XML document.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-3153485182187112589?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/3153485182187112589/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=3153485182187112589' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/3153485182187112589'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/3153485182187112589'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/06/select-xml-and-nested-xpath-evaluations.html' title='Select-Xml and nested XPath evaluations'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-8738823901015148037</id><published>2011-06-30T22:13:00.002+01:00</published><updated>2011-06-30T22:16:55.297+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Mileage'/><category scheme='http://www.blogger.com/atom/ns#' term='cycling'/><title type='text'>Target Achieved</title><content type='html'>&lt;p&gt;Arriving home this evening, the bike odometer read 1906.8 miles since I installed it at the end of the first week in July last year.  Adding the 97 miles I logged on my spring holiday on a hired bike, that pushes me over the 2000 miles mark. for the year (with 8 days to spare).&lt;/p&gt;
&lt;p&gt;So, onwards to 3000 miles by year's end.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-8738823901015148037?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/8738823901015148037/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=8738823901015148037' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/8738823901015148037'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/8738823901015148037'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/06/target-achieved.html' title='Target Achieved'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-2299649732117130346</id><published>2011-06-22T18:14:00.011+01:00</published><updated>2011-06-22T19:27:33.055+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='PowerShell'/><title type='text'>More PowerShell, XML and XPath -- Select-Xml and multi-node selections</title><content type='html'>&lt;p&gt;Let's start with &lt;a href="http://stevegilham.blogspot.com/2011/06/powershell-xml-and-xpath.html"&gt;the same document as before&lt;/a&gt;.  Now at PowerShell 2, we can also do&lt;/p&gt;
&lt;pre class="brush: c#"&gt;&amp;gt;$n = Select-Xml -Xml $x -XPath "//Rule[@Name='FileHeaderMustShowCopyright']//BooleanProperty[@Name='Enabled']"
&amp;gt;$n

Node                                    Path                                    Pattern
----                                    ----                                    -------
BooleanProperty                         InputStream                             //Rule[@Name='FileHeaderMustShowCopy...

&amp;gt;$n.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     False    SelectXmlInfo                            System.Object
&amp;gt;$n.Node

Name                                                        #text
----                                                        -----
Enabled                                                     False

&amp;gt;$n.Node.ParentNode.ParentNode

Name                                                        RuleSettings
----                                                        ------------
FileHeaderMustShowCopyright                                 RuleSettings&lt;/pre&gt;
&lt;p&gt;which avoids the explicit call to the .net infrastructure, but wraps up the actual content we got in the previous example as the &lt;code&gt;Node&lt;/code&gt; field inside an object.  This is not so much of a problem when picking a single node, but when you want to do the equivalent of &lt;code&gt;SelectNodes&lt;/code&gt; nested (e.g. for each Rule, do something with each setting) some disassembly is required in order to perform the inner selection:&lt;/p&gt;
&lt;pre class="brush: c#"&gt;&amp;gt;$s = Select-Xml -Xml $x -XPath '//Rule'
&amp;gt;$s | foreach-object { $_.Node ; Select-Xml $_.Node -XPath 'descendant::BooleanProperty' |  foreach-object { $_.Node } }

Name                                                        RuleSettings
----                                                        ------------
FileMustHaveHeader                                          RuleSettings
Enabled
FileHeaderMustShowCopyright                                 RuleSettings
Enabled&lt;/pre&gt;
&lt;p&gt;compare&lt;/p&gt;
&lt;pre class="brush: c#"&gt;&amp;gt;$nn = $x.SelectNodes("//Rule")
&amp;gt;$nn | ForEach-Object { $_ ; $_.SelectNodes("descendant::BooleanProperty") }

Name                                                        RuleSettings
----                                                        ------------
FileMustHaveHeader                                          RuleSettings
Enabled
FileHeaderMustShowCopyright                                 RuleSettings
Enabled&lt;/pre&gt;
&lt;p&gt;It is, however, a bit of an oversight that the &lt;code&gt;Select-Xml&lt;/code&gt; cmdlet doesn't consume &lt;code&gt;SelectXmlInfo&lt;/code&gt; objects through &lt;a href="http://msdn.microsoft.com/en-us/library/system.management.automation.argumenttransformationattribute%28v=VS.85%29.aspx"&gt;the usual type conversion mechanisms&lt;/a&gt; inside the cmdlet infrastructure:&lt;/p&gt;
&lt;pre class="brush: c#"&gt;&amp;gt;$s | foreach-object { Select-Xml -xml $_ -XPath 'descendant::BooleanProperty' |  foreach-object { $_.Node } }
Select-Xml : Cannot bind parameter 'Xml'. Cannot convert the "&amp;lt;RuleSettings&amp;gt;&amp;lt;BooleanProperty Name="Enabled"&amp;gt;False&amp;lt;/Bool
eanProperty&amp;gt;&amp;lt;/RuleSettings&amp;gt;" value of type "Microsoft.PowerShell.Commands.SelectXmlInfo" to type "System.Xml.XmlNode".
At line:1 char:38
+ $s | foreach-object { Select-Xml -xml &amp;lt;&amp;lt;&amp;lt;&amp;lt;  $_ -XPath '//BooleanProperty' |  foreach-object { $_.Node } }
    + CategoryInfo          : InvalidArgument: (:) [Select-Xml], ParameterBindingException
    + FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.PowerShell.Commands.SelectXmlCommand

Select-Xml : Cannot bind parameter 'Xml'. Cannot convert the "&amp;lt;RuleSettings&amp;gt;&amp;lt;BooleanProperty Name="Enabled"&amp;gt;False&amp;lt;/Bool
eanProperty&amp;gt;&amp;lt;/RuleSettings&amp;gt;" value of type "Microsoft.PowerShell.Commands.SelectXmlInfo" to type "System.Xml.XmlNode".
At line:1 char:38
+ $s | foreach-object { Select-Xml -xml &amp;lt;&amp;lt;&amp;lt;&amp;lt;  $_ -XPath '//BooleanProperty' |  foreach-object { $_.Node } }
    + CategoryInfo          : InvalidArgument: (:) [Select-Xml], ParameterBindingException
    + FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.PowerShell.Commands.SelectXmlCommand&lt;/pre&gt;
&lt;p&gt;but once you know there's that gotcha, it can be worked around.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-2299649732117130346?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/2299649732117130346/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=2299649732117130346' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/2299649732117130346'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/2299649732117130346'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/06/more-powershell-xml-and-xpath-select.html' title='More PowerShell, XML and XPath -- Select-Xml and multi-node selections'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-133462551704069287</id><published>2011-06-19T22:21:00.004+01:00</published><updated>2011-06-22T18:14:17.578+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='PowerShell'/><title type='text'>PowerShell, XML and XPath</title><content type='html'>&lt;p&gt;Some of the time, PowerShell makes picking apart an XML file nice and easy by providing properties on the XML object matching nodes; but inevitably you get to a point where there are more than one node of a given type as the child of the one you are at.  So then it's time to break out the XPath.&lt;/p&gt;
&lt;p&gt;Take a sample XML file (this one being a StyleCop settings file, being to hand, and complicated enough to be interesting):&lt;/p&gt;
&lt;pre class="brush: xml"&gt;&amp;lt;StyleCopSettings Version="4.3"&amp;gt;
  &amp;lt;Analyzers&amp;gt;
    &amp;lt;Analyzer AnalyzerId="Microsoft.StyleCop.CSharp.DocumentationRules"&amp;gt;
      &amp;lt;Rules&amp;gt;
        &amp;lt;Rule Name="FileMustHaveHeader"&amp;gt;
          &amp;lt;RuleSettings&amp;gt;
            &amp;lt;BooleanProperty Name="Enabled"&amp;gt;False&amp;lt;/BooleanProperty&amp;gt;
          &amp;lt;/RuleSettings&amp;gt;
        &amp;lt;/Rule&amp;gt;
        &amp;lt;Rule Name="FileHeaderMustShowCopyright"&amp;gt;
          &amp;lt;RuleSettings&amp;gt;
            &amp;lt;BooleanProperty Name="Enabled"&amp;gt;False&amp;lt;/BooleanProperty&amp;gt;
          &amp;lt;/RuleSettings&amp;gt;
        &amp;lt;/Rule&amp;gt;
      &amp;lt;/Rules&amp;gt;
      &amp;lt;AnalyzerSettings /&amp;gt;
    &amp;lt;/Analyzer&amp;gt;
  &amp;lt;/Analyzers&amp;gt;
&amp;lt;/StyleCopSettings&amp;gt;&lt;/pre&gt;
&lt;p&gt;And we want to extract the "FileHeaderMustShowCopyright" enabled property.  So we can do something like this:&lt;/p&gt;
&lt;pre class="brush: c#"&gt;&amp;gt;[xml] $x = Get-Content "Settings.StyleCop"
&amp;gt;$x.stylecopsettings.Analyzers.Analyzer.Rules.SelectSingleNode("Rule[@Name='FileHeaderMustShowCopyright']").RuleSettings.SelectSingleNode("BooleanProperty[@Name='Enabled']")

Name                                                        #text
----                                                        -----
Enabled                                                     False


&amp;gt;&lt;/pre&gt;
&lt;p&gt;using PowerShell to navigate where there is no ambiguity, and XPath at each for in the road; or&lt;/p&gt;
&lt;pre class="brush: c#"&gt;&amp;gt;$x.SelectSingleNode("//Rule[@Name='FileHeaderMustShowCopyright']").RuleSettings.SelectSingleNode("BooleanProperty[@Name='Enabled']")&lt;/pre&gt;
&lt;p&gt;or&lt;/p&gt;
&lt;pre class="brush: c#"&gt;&amp;gt;$x.SelectSingleNode("//Rule[@Name='FileHeaderMustShowCopyright']").SelectSingleNode("//BooleanProperty[@Name='Enabled']")&lt;/pre&gt;
&lt;p&gt;or simply&lt;/p&gt;
&lt;pre class="brush: c#"&gt;&amp;gt;$x.SelectSingleNode("//Rule[@Name='FileHeaderMustShowCopyright']//BooleanProperty[@Name='Enabled']")&lt;/pre&gt;
&lt;p&gt;depending how many of the steps above where we want to end up are of interest.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-133462551704069287?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/133462551704069287/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=133462551704069287' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/133462551704069287'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/133462551704069287'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/06/powershell-xml-and-xpath.html' title='PowerShell, XML and XPath'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-3496024016486590761</id><published>2011-06-17T22:50:00.004+01:00</published><updated>2011-06-17T23:22:29.624+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='PowerShell'/><title type='text'>Splitting Pascal-Cased names in PowerShell</title><content type='html'>&lt;p&gt;Something I needed to whip up recently, now slightly more polished.  Starting with Jon Galloway's &lt;a href="http://weblogs.asp.net/jgalloway/archive/2005/09/27/426087.aspx"&gt;Splitting Camel Case with RegEx&lt;/a&gt;, and transposing to PowerShell's built-in regex syntax, the core operation becomes like&lt;/p&gt;
&lt;pre class="brush: c#"&gt;("ZomgWtfBbq" -creplace '[A-Z]', ' $&amp;').Trim().Split($null)&lt;/pre&gt;
&lt;p&gt;where we have to use the case sensitive &lt;code&gt;-creplace&lt;/code&gt; operator to be able to insert spaces before capital letters.  This outputs&lt;/p&gt;
&lt;pre class="code"&gt;Zomg
Wtf
Bbq&lt;/pre&gt;
&lt;p&gt;Wrapping it up into a function that handles a general batch of strings or things looks like this&lt;/p&gt;
&lt;pre class="brush: c#"&gt;function PascalSplit {
  $args | ForEach-Object {
    if ($_ -is [array]) { 
      $_ | ForEach-Object { PascalSplit $_ }
    } else {
      ($_.ToString() -creplace '[A-Z]', ' $&amp;').Trim().Split($null)
    }  
  }
}&lt;/pre&gt;
&lt;p&gt;so we can do this:&lt;/p&gt;
&lt;pre class="code"&gt;&amp;gt;PascalSplit @("ZomgWtfBbq", "ShazBot")
Zomg
Wtf
Bbq
Shaz
Bot
&amp;gt;&lt;/pre&gt;
&lt;p&gt;flattening the string array as we go (there is probably a better way to do that, but this works).&lt;/p&gt;
&lt;p&gt;Following the link to &lt;a href="http://popcyclical.com/2010/09/12/SplittingPascalCamelCaseWithRegExEnhancements.aspx"&gt;James "poprhythm" Kolpack's enhancement&lt;/a&gt;, we can upgrade the simple function with his look-behind extensions to handle TLAs in names:&lt;/p&gt;
&lt;pre class="brush: c#"&gt;function PascalSplit {
  $args | ForEach-Object {
    if ($_ -is [array]) { 
      $_ | ForEach-Object { PascalSplit $_ }
    } else {
      ($_.ToString() -creplace '(?&amp;lt;!^)([A-Z][a-z]|(?&amp;lt;=[a-z])[A-Z])', ' $&amp;').Split($null)
    }  
  }
}&lt;/pre&gt;
&lt;p&gt;which we can test with&lt;/p&gt;
&lt;pre class="code"&gt;&amp;gt;PascalSplit LearnWCFInSixEasyMonths
Learn
WCF
In
Six
Easy
Months
&amp;gt;$a = PascalSplit @("ZomgWtfBbq", "ShazBot", "LearnWCFInSixEasyMonths")
&amp;gt;$a
Zomg
Wtf
Bbq
Shaz
Bot
Learn
WCF
In
Six
Easy
Months
&amp;gt;$a.Length
11
&amp;gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-3496024016486590761?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/3496024016486590761/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=3496024016486590761' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/3496024016486590761'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/3496024016486590761'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/06/splitting-pascal-cased-names-in.html' title='Splitting Pascal-Cased names in PowerShell'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-5748778914627115554</id><published>2011-06-04T08:24:00.005+01:00</published><updated>2011-06-17T23:37:31.496+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='don&apos;t talk to me about life'/><title type='text'>And he still thinks "Referism" would work</title><content type='html'>&lt;p&gt;One of those rare political rants...&lt;/p&gt;
&lt;p&gt;&lt;a href="http://eureferendum.blogspot.com/2011/05/that-ism-again.html"&gt;Referism&lt;/a&gt; -- the quaint notion that a yearly budget referendum would do anything to check the ruling class.&lt;/p&gt;
&lt;p&gt;The EU Referendum blog has started pushing this idea recently -- and in a massive act of cognitive dissonance does so while reporting how, when faced with a tight budget, &lt;a href="http://eureferendum.blogspot.com/2011/06/more-thievery.html"&gt;the pols will protect their core function of providing sinecures to the &lt;span style="font-style:italic;"&gt;nomenklatura&lt;/span&gt;&lt;/a&gt;, and trim the fluff at the margins (like providing services for the &lt;span style="font-style:italic;"&gt;hoi polloi&lt;/span&gt;). And if that had been voted on, then next year, after parading the bleeding stumps, they'd come back for more ("or we'll kill this kitten/shut this library/sack these nurses...").  And that's assuming they don't take the get-out clause of Parliament being unable to bind itself and pass whatever emergency legislation to bypass.&lt;/p&gt;
&lt;p&gt;The key problems are that politicians lie, money is fungible, and any stated budget breakdowns would be worth exactly as much as manifesto commitments are under universal suffrage. Oh, and it cuts out all the persiflage in the way of the root problem with democracy:&lt;/p&gt;
&lt;blockquote cite="http://www.lorencollins.net/tytler.html" title="Attribution uncertain -- Alexander Tytler. Or Alexander Tyler. Or Arnold Toynbee. Or Lord Thomas Macaulay. Or..."&gt;&lt;p&gt;"A democracy cannot survive as a permanent form of government. It can last only until its citizens discover that they can vote themselves largesse from the public treasury."&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Even a radical replacement like having MPs and Senators chosen by lot from the populace -- a way of taking power from those who most obviously actively seek it -- would still leave the problem of the entrenched Civil Service and the quangocrats.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-5748778914627115554?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/5748778914627115554/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=5748778914627115554' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/5748778914627115554'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/5748778914627115554'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/06/and-he-still-thinks-referism-would-work.html' title='And he still thinks &quot;Referism&quot; would work'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-8048698897496098844</id><published>2011-05-31T22:54:00.005+01:00</published><updated>2011-05-31T23:53:19.549+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>CodeDOM and poor man's lambdas -- Part 2</title><content type='html'>&lt;p&gt;Unfortunately there is one major sticking point in getting the generation to work for F# -- local variables are declared as &lt;code&gt;let mutable&lt;/code&gt; rather than as a &lt;code&gt;ref&lt;/code&gt; type which means that the necessary closure cannot be made; this is rather stronger than the lack of support for &lt;code&gt;CodeDefaultValueExpression&lt;/code&gt;, which can be fudged around, or for nested types (which just become mutually recursive), though with up-front decision as to the language to generate (rather than making the choice following the expression tree), we could replace problematic elements with snippets.&lt;/p&gt;
&lt;p&gt;That aside, the main operation looks like this, similar to the previous examples&lt;/p&gt;
&lt;pre class="brush: f#"&gt;let GenerateWrapper (target:Type) =

  // Create a compile unit with a namespace
  let compileUnit = new CodeCompileUnit()
  let scope = new CodeNamespace("Derived")
  compileUnit.Namespaces.Add( scope ) |&amp;gt; ignore
  
  // Create a GeneratedCodeAttribute expression
  let generated = typeof&amp;lt;GeneratedCodeAttribute&amp;gt;
  let ga = new CodeAttributeDeclaration(TypeRef generated)
  ga.Arguments.Add(new CodeAttributeArgument( new CodePrimitiveExpression("Generator"))) |&amp;gt; ignore
  ga.Arguments.Add(new CodeAttributeArgument( new CodePrimitiveExpression("0.0.0.0"))) |&amp;gt; ignore
  
  // Create the wrapper type and attribute it
  let wrapper = new CodeTypeDeclaration(target.Name + "Wrapper")
  wrapper.TypeAttributes &amp;lt;- TypeAttributes.Public ||| TypeAttributes.Sealed
  wrapper.CustomAttributes.Add(ga) |&amp;gt; ignore
  scope.Types.Add(wrapper) |&amp;gt; ignore
  
  // Add a field for the wrapped object
  let this = new CodeMemberField(TypeRef target, "this")
  wrapper.Members.Add(this) |&amp;gt; ignore
  
  // Create a simple constructor to set the wrapped object
  let construct = new CodeConstructor()
  construct.Parameters.Add(new CodeParameterDeclarationExpression(TypeRef target, target.Name)) |&amp;gt; ignore
  let self = new CodeThisReferenceExpression()
  let atThis = new CodeFieldReferenceExpression(self, "this")
  let assign = new CodeAssignStatement(atThis, new CodeArgumentReferenceExpression(target.Name))
  construct.Statements.Add(assign) |&amp;gt; ignore
  wrapper.Members.Add(construct) |&amp;gt; ignore  
  
  // Create a set to hold namespaces
  let namespaces = new HashSet&amp;lt;string&amp;gt;() 
       
  // Get all the methods of interest (properties would be similar)
  let methods = target.GetMethods(BindingFlags.Instance ||| BindingFlags.Public ||| BindingFlags.DeclaredOnly)
                |&amp;gt; Seq.filter (fun x -&amp;gt; not x.IsSpecialName )
                |&amp;gt; Seq.sortBy (fun p -&amp;gt; p.Name)
      
  
  // Accumulate all referenced namespaces (aside from generic types)          
  methods
  |&amp;gt; Seq.iter (fun x -&amp;gt; namespaces.Add(x.ReturnType.Namespace) |&amp;gt; ignore)
                     
  methods
  |&amp;gt; Seq.collect (fun m -&amp;gt; m.GetParameters())
  |&amp;gt; Seq.iter (fun x -&amp;gt; namespaces.Add(x.ParameterType.Namespace) |&amp;gt; ignore)
       
  // Generate a proxy class for each call    
  methods
  |&amp;gt; Seq.map (GenerateClass target ga)
  |&amp;gt; Seq.map wrapper.Members.Add
  |&amp;gt; Seq.iter ignore
  
  // Generate a delegating method for each call
  methods
  |&amp;gt; Seq.map (GenerateProxy target)
  |&amp;gt; Seq.map wrapper.Members.Add
  |&amp;gt; Seq.iter ignore  
  
  // Add in all the namespaces
  namespaces.Add(target.Namespace) |&amp;gt; ignore
  namespaces.Add(generated.Namespace) |&amp;gt; ignore
  namespaces
  |&amp;gt; Seq.sort
  |&amp;gt; Seq.iter (fun n-&amp;gt; scope.Imports.Add(new CodeNamespaceImport(n)))
  compileUnit&lt;/pre&gt;
&lt;p&gt;If we eschewed F# support entirely, and used a partial class, then we could skip the constructor and field declarations and provide the input by any other mechanism of our choice in a hand-written part.&lt;/p&gt;
&lt;p&gt;Generating the closure classes is a simple matter -- especially as the names of the parameters can be used as fields directly without any sigils, provided that they never contain the &lt;code&gt;@this&lt;/code&gt; or &lt;code&gt;proxy&lt;/code&gt; name by convention.  This makes the  closure class approach slightly simpler than a snippet driven use of direct lambda syntax, where local variable names to hold out parameters would in general have to be generated so as  not to clash with any of the arguments:&lt;/p&gt;
&lt;pre class="brush: f#"&gt;let GenerateClass (target:Type) (ga:CodeAttributeDeclaration)(m:MethodInfo) =
  // Generate the type and mark as generated
  let newtype = new CodeTypeDeclaration(m.Name + "__Proxy")
  newtype.TypeAttributes &amp;lt;- TypeAttributes.NestedPrivate /// .NotPublic ||| TypeAttributes.Sealed
  newtype.CustomAttributes.Add(ga) |&amp;gt; ignore
  
  // add a field to point to the wrapped object
  let this = new CodeMemberField(TypeRef target, "this")
  this.Attributes &amp;lt;- MemberAttributes.Public
  newtype.Members.Add(this) |&amp;gt; ignore
  
  // Fields to allow us to close over all the parameters
  m.GetParameters()
  |&amp;gt; Seq.map GenerateField
  |&amp;gt; Seq.map newtype.Members.Add
  |&amp;gt; Seq.iter ignore
  
  // The lambda method
  let proxy = new CodeMemberMethod()
  proxy.Name &amp;lt;- m.Name
  proxy.Attributes &amp;lt;- MemberAttributes.Public ||| MemberAttributes.Final
  
  // Fudge the case of a void method
  proxy.ReturnType &amp;lt;- TypeRef(m.ReturnType)
  if m.ReturnType = typeof&amp;lt;System.Void&amp;gt; then proxy.ReturnType &amp;lt;- TypeRef(typeof&amp;lt;int&amp;gt;)
  
  // Build the call to the wrapped object
  let self = new CodeThisReferenceExpression()
  let atThis = new CodeFieldReferenceExpression(self, "this")
  let call = new CodeMethodReferenceExpression(atThis, m.Name)
    
  let parameters = m.GetParameters()
                   |&amp;gt; Seq.map (fun x -&amp;gt; let ex = new CodeFieldReferenceExpression(self, x.Name)
                                        let dir = if x.IsIn &amp;&amp; x.IsOut then FieldDirection.Ref
                                                  else if x.IsOut then FieldDirection.Out 
                                                  else FieldDirection.In
                                        new CodeDirectionExpression(dir, ex) :&amp;gt; CodeExpression)
                   |&amp;gt; Seq.toArray

  let invoke = new CodeMethodInvokeExpression(call, parameters)
  
  // return a dummy zero instead of void, or the result
  if m.ReturnType = typeof&amp;lt;System.Void&amp;gt; then 
    proxy.Statements.Add invoke |&amp;gt; ignore
    let result = new CodeMethodReturnStatement(new CodePrimitiveExpression(0))
    proxy.Statements.Add  result |&amp;gt; ignore
  else
    proxy.Statements.Add  (new CodeMethodReturnStatement(invoke)) |&amp;gt; ignore
    
  // add this method to the class
  newtype.Members.Add(proxy) |&amp;gt; ignore
 
  newtype&lt;/pre&gt;
&lt;p&gt;where the individual field declarations have to work around the decorations for &lt;code&gt;out&lt;/code&gt; or &lt;code&gt;ref&lt;/code&gt;, thus:&lt;/p&gt;
&lt;pre class="brush: f#"&gt;let GenerateField (p:ParameterInfo) =
  let t = if p.ParameterType.IsByRef then p.ParameterType.GetElementType() else p.ParameterType
  let field = new CodeMemberField(TypeRef t, p.Name)
  field.Attributes &amp;lt;- MemberAttributes.Public
  field&lt;/pre&gt;
&lt;p&gt;The generation of the proxy methods is equally mechanical&lt;/p&gt;
&lt;pre class="brush: f#"&gt;let GenerateProxy (target:Type) (m:MethodInfo) =
  // Generate the method and its parameter list
  let proxy = new CodeMemberMethod()
  proxy.Name &amp;lt;- m.Name
  proxy.Attributes &amp;lt;- MemberAttributes.Public ||| MemberAttributes.Final
  proxy.ReturnType &amp;lt;- TypeRef(m.ReturnType)
  m.GetParameters()
  |&amp;gt; Seq.iter ( fun (t:ParameterInfo) -&amp;gt; 
                let px = new CodeParameterDeclarationExpression()
                px.Direction &amp;lt;- if t.IsIn &amp;&amp; t.IsOut then FieldDirection.Ref
                                else if t.IsOut then FieldDirection.Out 
                                else FieldDirection.In
                px.Name &amp;lt;- t.Name
                let tt = if t.ParameterType.IsByRef then t.ParameterType.GetElementType() else t.ParameterType
                px.Type &amp;lt;- TypeRef(tt)
                proxy.Parameters.Add(px) |&amp;gt; ignore
               )
  
  // construct the closure object -- the value is "let mutable" in F#
  let proxyType = new CodeTypeReference(m.Name + "__Proxy")
  let construct = new CodeVariableDeclarationStatement(proxyType, "proxy", 
                         CodeObjectCreateExpression((m.Name + "__Proxy"), [||]))
  proxy.Statements.Add construct |&amp;gt; ignore
  
  // create a reference to the closure
  let self = new CodeThisReferenceExpression()
  let atThis = new CodeFieldReferenceExpression(self, "this")
  let proxyRef = CodeVariableReferenceExpression("proxy")
                
  // initialize its reference to the wrapped object
  let assign = new CodeAssignStatement( new CodeFieldReferenceExpression(proxyRef, "this"), atThis)
  proxy.Statements.Add assign |&amp;gt; ignore
  
  // initialize all the other fields (to default if out only)
  m.GetParameters()
  |&amp;gt; Seq.map (fun x -&amp;gt; let left = new CodeFieldReferenceExpression(proxyRef, x.Name)
                       let right = if x.IsOut &amp;&amp; (not x.IsIn) then 
                                      // Doesn't work in the F# CodeDOM
                                      new CodeDefaultValueExpression(TypeRef (x.ParameterType.GetElementType())) :&amp;gt; CodeExpression
                                   else 
                                      new CodeArgumentReferenceExpression(x.Name) :&amp;gt; CodeExpression
                       new CodeAssignStatement(left, right)
             )
  |&amp;gt; Seq.map proxy.Statements.Add
  |&amp;gt; Seq.iter ignore
  
  // Expect the lambda to return int for a void method
  let typeRef = if m.ReturnType = typeof&amp;lt;System.Void&amp;gt; then TypeRef(typeof&amp;lt;int&amp;gt;)
                else TypeRef(m.ReturnType)
  
  // call the Wrapping method which takes the lambda
  let funcType = CodeTypeReference("Func")
  funcType.TypeArguments.Add typeRef |&amp;gt; ignore
  
  // because proxyRef is "let mutable" in F#, this attempt to close over it here is a compiler error in F#, but not in C#
  let func = CodeObjectCreateExpression(funcType, new CodeMethodReferenceExpression(proxyRef, m.Name))
  let computing = new CodeMethodInvokeExpression(
                    new CodeTypeReferenceExpression("Wrapper"),
                    "LogCall", 
                    new CodePrimitiveExpression(m.Name),
                    func)
  
  // and store the result
  let returnValue = new CodeVariableDeclarationStatement(typeRef, "return", computing)

  proxy.Statements.Add returnValue |&amp;gt; ignore
  
  // copy back all the out or ref parameters
  m.GetParameters()
  |&amp;gt; Seq.filter (fun x -&amp;gt; x.IsOut)
  |&amp;gt; Seq.map (fun x -&amp;gt; let left = new CodeArgumentReferenceExpression(x.Name)
                       let right = new CodeFieldReferenceExpression(proxyRef, x.Name)
                       new CodeAssignStatement(left, right)
             )
  |&amp;gt; Seq.map proxy.Statements.Add
  |&amp;gt; Seq.iter ignore
  
  // and for a non-void method, return the value from the lambda
  if m.ReturnType &amp;lt;&amp;gt; typeof&amp;lt;System.Void&amp;gt; then 
    let r = new CodeMethodReturnStatement(CodeVariableReferenceExpression("return"))
    proxy.Statements.Add r |&amp;gt; ignore
  
  proxy&lt;/pre&gt;
&lt;p&gt;So, given a simple type&lt;/p&gt;
&lt;pre class="brush: c#"&gt;    public class Subject
    {
        public int DoIt(int argument, out string result) {...}

        public void DoItSilently(int argument, out string result) {...}
    }&lt;/pre&gt;
&lt;p&gt;we generate&lt;/p&gt;
&lt;pre class="brush: c#"&gt;namespace Derived {
    using Base;
    using System;
    using System.CodeDom.Compiler;
    
    
    [GeneratedCodeAttribute("Generator", "0.0.0.0")]
    public sealed class SubjectWrapper {
        
        private Subject @this;
        
        private SubjectWrapper(Subject Subject) {
            this.@this = Subject;
        }
        
        public int DoIt(int argument, out string result) {
            DoIt__Proxy proxy = new DoIt__Proxy();
            proxy.@this = this.@this;
            proxy.argument = argument;
            proxy.result = default(string);
            int @return = Wrapper.LogCall("DoIt", new Func&amp;lt;int&amp;gt;(proxy.DoIt));
            result = proxy.result;
            return @return;
        }
        
        public void DoItSilently(int argument, out string result) {
            DoItSilently__Proxy proxy = new DoItSilently__Proxy();
            proxy.@this = this.@this;
            proxy.argument = argument;
            proxy.result = default(string);
            int @return = Wrapper.LogCall("DoItSilently", new Func&amp;lt;int&amp;gt;(proxy.DoItSilently));
            result = proxy.result;
        }
        
        [GeneratedCodeAttribute("Generator", "0.0.0.0")]
        private class DoIt__Proxy {
            public Subject @this;
            public int argument;
            public string result;
            public int DoIt() {
                return this.@this.DoIt(this.argument, out this.result);
            }
        }
        
        [GeneratedCodeAttribute("Generator", "0.0.0.0")]
        private class DoItSilently__Proxy {
            public Subject @this;
            public int argument;
            public string result;
            public int DoItSilently() {
                this.@this.DoItSilently(this.argument, out this.result);
                return 0;
            }
        }
    }
}&lt;/pre&gt;
&lt;p&gt;The F# code contains&lt;/p&gt;
&lt;pre class="brush: f#"&gt;                let mutable (proxy:SubjectWrapper_DoIt__Proxy) = new SubjectWrapper_DoIt__Proxy()
...
                proxy.result &lt;- (* Unknown expression type 'CodeDefaultValueExpression' please report this to the F# team. *)&lt;/pre&gt;
&lt;p&gt;Manually replacing this with&lt;/p&gt;
&lt;pre class="brush: f#"&gt;                let mutable (proxy:SubjectWrapper_DoIt__Proxy) = new SubjectWrapper_DoIt__Proxy()
...
                proxy.result &lt;- Unchecked.defaultof&amp;lt;string&amp;gt;&lt;/pre&gt;
&lt;p&gt;then shows up on the next line&lt;/p&gt;
&lt;pre class="brush: f#"&gt;                let mutable (_return:int) = Wrapper.LogCall("DoIt", new Func&lt;int&gt;(proxy.DoIt))&lt;/pre&gt;
&lt;p&gt;as an error &lt;code&gt;The mutable variable 'proxy' is used in an invalid way. Mutable variables cannot be captured by closures. Consider eliminating this use of mutation or using a heap-allocated mutable reference cell via 'ref' and '!'.&lt;/code&gt;; and as there are no &lt;code&gt;readonly&lt;/code&gt; locals in the CLR -- it's all F# compiler magic that gives the illusion of same -- we're stuck with no control to tweak to make &lt;code&gt;proxy&lt;/code&gt; immutable in the F# output.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-8048698897496098844?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/8048698897496098844/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=8048698897496098844' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/8048698897496098844'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/8048698897496098844'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/05/codedom-and-poor-mans-lambdas-part-2.html' title='CodeDOM and poor man&apos;s lambdas -- Part 2'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-5403508802178642967</id><published>2011-05-30T17:32:00.009+01:00</published><updated>2011-05-30T20:07:02.373+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>CodeDOM and poor man's lambdas -- Part 1</title><content type='html'>&lt;p&gt;The CodeDOM expression methods are stuck at the level of .net 2.0, and look unlikely to grow further support for further syntax outside of feeding it in as literal strings using the &lt;code&gt;System.CodeDom.CodeSnippet*&lt;/code&gt; classes -- which takes away the cross-language spirit of the whole library.&lt;/p&gt;
&lt;p&gt;So, what to do when we want to generate code containing lambdas?&lt;/p&gt;
&lt;p&gt;Well, the obvious way is to strip away the syntactic sugar involved in the lambda/closure notation and generate explicitly what the compiler is doing for you under the covers anyway.&lt;/p&gt;
&lt;p&gt;So, to take a case which is relevant to my interests, if we have a method (of static type &lt;code&gt;Wrapper&lt;/code&gt;, say) --&lt;/p&gt;
&lt;pre class="brush: c#"&gt;        public static T LogCall&amp;lt;T&amp;gt;(string name, Func&amp;lt;T&amp;gt; call)&lt;/pre&gt;
&lt;p&gt;that will perform some pre- and post- call logging around invoking the delegate; and we have a method
&lt;pre class="brush: c#"&gt;        public int DoIt(int argument, out string result)&lt;/pre&gt; 
&lt;p&gt;which we want to invoke through the &lt;code&gt;LogCall&lt;/code&gt; method (considered as a decorator to the action).  Writing this by hand, we would want a decorator type containing a matching method something like this:&lt;/p&gt;
&lt;pre class="brush: c#"&gt;        public int DoIt(int argument, out string result)
        {
            string local_result = default(string);
            var computed = Wrapper.LogCall("DoIt", () =&gt; wrapped.DoIt(argument, out local_result));
            result = local_result;
            return computed;
        }&lt;/pre&gt;
&lt;p&gt;which exposes the same interface, and delegates to the real logic in the contained object &lt;code&gt;wrapped&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If we examine the code that is actually generated by compiling this, we see that the whole closure is replaced by a synthetic private inner class with a public field for each object closed over -- in this case all the arguments to the &lt;code&gt;DoIt&lt;/code&gt; method, plus a &lt;code&gt;this&lt;/code&gt; reference for the decorator -- and a method that stands for the lambda, which, generated names aside, looks like:&lt;/p&gt;
&lt;pre class="brush: c#"&gt;        public int DoIt()
        {
                return this.@this.wrapped.DoIt(argument, out result);
        }&lt;/pre&gt;
&lt;p&gt;while the wrapping method looks like&lt;/p&gt;
&lt;pre class="brush: c#"&gt;        public int DoIt(int argument, out string result)
        {
            var proxy = new DoItProxy();
            proxy.result = default(string);
            proxy.argument = argument;
            proxy.@this = this;
            var computed = Wrapper.LogCall("DoIt", new Func&amp;lt;int&amp;gt;(proxy.DoIt));
            result = proxy.result;
            return computed;
        }&lt;/pre&gt;
&lt;p&gt;And now, while the analogous F# code would probably return an &lt;code&gt;int * string&lt;/code&gt; tuple, rather than an &lt;code&gt;out&lt;/code&gt; parameter for multiple returns, the equivalent code snippet will do the job, just changing the types of the &lt;code&gt;Func&lt;/code&gt; object; and the literal transposition of the C# code, &lt;code&gt;out&lt;/code&gt; parameters and all, should still work, cross language.&lt;/p&gt;
&lt;p&gt;In the more general case, of lambdas with arguments, then the method in the proxy type has the same arguments as the lambda needs; and in the case where no variables are being closed over e.g.&lt;/p&gt;
&lt;pre class="brush: c#"&gt;        Func&amp;lt;int, int&amp;gt; square = x =&amp;gt; x*x;&lt;/pre&gt;
&lt;p&gt;there is an optimization that the supporting method can be made static on the enclosing type, rather than requiring an object to hold the closure references.&lt;/p&gt;
&lt;p&gt;In part 2, having simplified the problem, we can move on to using the CodeDOM to generate the &lt;code&gt;LogCall&lt;/code&gt; decorating method and closure, given a &lt;code&gt;MethodInfo&lt;/code&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-5403508802178642967?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/5403508802178642967/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=5403508802178642967' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/5403508802178642967'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/5403508802178642967'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/05/codedom-and-poor-mans-lambdas-part-1.html' title='CodeDOM and poor man&apos;s lambdas -- Part 1'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-6808657673840340842</id><published>2011-05-29T09:49:00.009+01:00</published><updated>2011-05-29T21:53:23.481+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>C# under the covers -- Enumerators</title><content type='html'>&lt;p&gt;Following up on &lt;a href="https://www.blogger.com/comment.g?blogID=5569894&amp;postID=4733268298667107098"&gt;the comment stream to an earlier post&lt;/a&gt;, a few little bits revealed by the investigations, with a little help from &lt;a href="http://wiki.sharpdevelop.net/ilspy.ashx"&gt;ILSpy&lt;/a&gt;:&lt;/p&gt;
&lt;pre class="brush: c#"&gt;yield break;&lt;/pre&gt;
&lt;p&gt;is the equivalent of &lt;/p&gt;
&lt;pre class="brush: c#"&gt;return;&lt;/pre&gt;
&lt;p&gt;inside an iterator function -- by which I mean that any silent exit from the method is compiled to the equivalent of an explicit &lt;code&gt;yield break;&lt;/code&gt;, and&lt;/p&gt;
&lt;pre class="brush: c#"&gt;foreach(T x in y) { ... }&lt;/pre&gt;
&lt;p&gt;on exit from the iteration calls the &lt;code&gt;Dispose()&lt;/code&gt; method of the &lt;code&gt;IEnumerator&amp;lt;T&amp;gt;&lt;/code&gt; it uses implicitly to iterate over the collection &lt;code&gt;y&lt;/code&gt;; whereas, of course, explicit use of the enumerator &lt;code&gt;MoveNext()&lt;/code&gt; and &lt;code&gt;Current&lt;/code&gt; do not.&lt;/p&gt;
&lt;p&gt;The behavioural constraints of &lt;code&gt;IEnumerable&lt;/code&gt; are weak enough as it is, being silent on whether replayability is part of the contract or not; this &lt;a href="http://stevegilham.blogspot.com/2011/04/f-under-covers-xiii-sequence.html"&gt;inconsistent disposal&lt;/a&gt; (with the &lt;code&gt;IDisposable&lt;/code&gt; interface being added only to the generic &lt;code&gt;IEnumerator&amp;lt;T&amp;gt;&lt;/code&gt; and not the .net 1.x vanilla version) makes guessing behaviours even worse when using LINQ methods like &lt;code&gt;Skip&lt;/code&gt; which does the dispose via &lt;code&gt;using&lt;/code&gt; when the iteration is exhausted&lt;/p&gt;
&lt;pre class="brush: c#"&gt;// System.Linq.Enumerable
private static IEnumerable&amp;lt;TSource&amp;gt; SkipIterator&amp;lt;TSource&amp;gt;(IEnumerable&amp;lt;TSource&amp;gt; source, int count)
{
 using (IEnumerator&amp;lt;TSource&amp;gt; enumerator = source.GetEnumerator())
 {
  while (count &amp;gt; 0 &amp;amp;&amp;amp; enumerator.MoveNext())
  {
   count--;
  }
  if (count &amp;lt;= 0)
  {
   while (enumerator.MoveNext())
   {
    yield return enumerator.Current;
   }
  }
 }
 yield break;
}&lt;/pre&gt;
&lt;p&gt;and &lt;code&gt;Take&lt;/code&gt;, which does it implicitly by the &lt;code&gt;foreach&lt;/code&gt; route before the iterator has been completely drained&lt;/p&gt;
&lt;pre class="brush: c#"&gt;// System.Linq.Enumerable
private static IEnumerablelt;TSource&amp;gt; TakeIteratorlt;TSource&amp;gt;(IEnumerablelt;TSource&amp;gt; source, int count)
{
 if (count &amp;gt; 0)
 {
  foreach (TSource current in source)
  {
   yield return current;
   if (--count == 0)
   {
    break;
   }
  }
 }
 yield break;
}&lt;/pre&gt;
&lt;p&gt;There are probably similar wonders waiting to be unearthed in the F# collections APIs as well.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-6808657673840340842?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/6808657673840340842/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=6808657673840340842' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/6808657673840340842'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/6808657673840340842'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/05/c-under-covers-enumerators.html' title='C# under the covers -- Enumerators'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-4518288158029327925</id><published>2011-05-27T18:44:00.010+01:00</published><updated>2011-05-27T20:51:59.425+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='anime'/><title type='text'>Belated Reviews — Anime</title><content type='html'>&lt;h4&gt;Otome Youkai Zakuro&lt;/h4&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/-iTnH_obbUeg/Td_y2do8pEI/AAAAAAAABBQ/7B7QeIgU2RU/s1600/otome-yokai-zakuro-screen.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 178px;" src="http://3.bp.blogspot.com/-iTnH_obbUeg/Td_y2do8pEI/AAAAAAAABBQ/7B7QeIgU2RU/s320/otome-yokai-zakuro-screen.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5611470678210159682" /&gt;&lt;/a&gt;
&lt;p&gt;Japan, about 100 years ago; and the pace of modernization is causing disturbances amongst the spirit folk.  So a Ministry of Spirit Affairs is set up, and a number young army officers assigned to liaise with representatives of the magical folk.  Being a shoujo, this means that there is a pretty fox girl (or two) for each of the handsome young men, including the eponymous Zakuro for Agemaki, the male lead, and all the necessary flowers and sparkles to show where True Love is blossoming.&lt;/p&gt;
&lt;p&gt;The series is best at the start -- introducing the characters, setting up the different romances, and nodding to the premise with a spirit-of-the-week to resolve, from the &lt;span style="font-style:italic;"&gt;kami&lt;/span&gt; whose shrine has been demolished to make way for a station hotel, to the mysterious presence haunting Agemaki's family home.  Alas, after the half-way point, it grows a LOLplot, involving Zakuro's mysterious past, and rather trips over itself -- the Serious Business being rather too much for the previously established light-hearted romance set-up.&lt;/p&gt;

&lt;h4&gt;Super Robot Wars OG &amp;#8212; The Inspector&lt;/h4&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/-sW3ZToshx1A/Td_1-B1CkII/AAAAAAAABBc/BsWLO91xdjs/s1600/super-robot-taisen-og-the-inspector-00008.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 180px;" src="http://4.bp.blogspot.com/-sW3ZToshx1A/Td_1-B1CkII/AAAAAAAABBc/BsWLO91xdjs/s320/super-robot-taisen-og-the-inspector-00008.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5611474106718523522" /&gt;&lt;/a&gt;
&lt;p&gt;Coming in from the cold to what is actually the second season of this title, I just rolled with it, figuring out as I went along which of the many factions were supposed to be the good guys and which the villains -- because I didn't find &lt;a href="http://www.mania.com/guide-to-super-robot-wars-original-generation_article_125709.html"&gt;this guide&lt;/a&gt; -- and just kicking back to watch the escalating battles, goofy robots, and Itano circus.&lt;/p&gt;
&lt;p&gt;This is what anime ought to be more like -- not the endless highschool rom-com, or cute girls being cute for the &lt;span style="font-style:italic;"&gt;otaku&lt;/span&gt; to perv over.&lt;/p&gt;

&lt;h4&gt;Letter Bee REVERSE&lt;/h4&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/-WRiUQbCY4kc/Td_4OtT_kbI/AAAAAAAABBo/9RhfiDuCInQ/s1600/TegamiBachiREVERSE1717.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 180px;" src="http://3.bp.blogspot.com/-WRiUQbCY4kc/Td_4OtT_kbI/AAAAAAAABBo/9RhfiDuCInQ/s320/TegamiBachiREVERSE1717.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5611476592292237746" /&gt;&lt;/a&gt;
&lt;p&gt;&lt;a href="http://stevegilham.blogspot.com/2010/04/anime-letter-bee.html"&gt;The first season&lt;/a&gt; ended on a cliff-hanger, where Lag's erstwhile mentor and role-model Gauche Suede appeared as one of the enemies of the whole Amberground system; and the second season carries on with the struggle between the Letter Bees, and the conspirators in REVERSE who are plotting to extinguish the artificial sun which lights this world (hinted to be Earth in some distant future).&lt;/p&gt;
&lt;p&gt;Surprisingly, for the thwarting of such an existential threat, there is little by way of revelation (we never see the sunlit lands of Akatsuki, only the twilight regions; the various hints at deeper things going on, the very nature of the artificial sun are raised as hints, but never revisited), revolution (we return pretty much to the &lt;span style="font-style:italic;"&gt;status quo ante&lt;/span&gt; after the whole secret war), or repercussion (after the threat is averted, all the elements that would disturb the &lt;span style="font-style:italic;"&gt;status quo&lt;/span&gt; simply migrate to a backwater town, while Lag returns to his normal rounds as a delivery boy).&lt;/p&gt;
&lt;p&gt;Definitely a case of better to travel hopefully that to arrive.&lt;/p&gt;

&lt;h4&gt;Level E&lt;/h4&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/-McaegWoGDnQ/Td_67_i4J0I/AAAAAAAABB0/k1Tn-XsQfw0/s1600/level-e-05-0017.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 180px;" src="http://1.bp.blogspot.com/-McaegWoGDnQ/Td_67_i4J0I/AAAAAAAABB0/k1Tn-XsQfw0/s320/level-e-05-0017.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5611479569303873346" /&gt;&lt;/a&gt;
&lt;p&gt;In what seems to be Japan's answer to &lt;span style="font-style:italic;"&gt;Men in Black&lt;/span&gt;, we have a setting where Earth is a way-station for many alien races, all staying undercover, in an uneasy truce despite existing enmities.  And then the Prince of the race in nominal charge of the planet comes to visit.&lt;/p&gt;
&lt;p&gt;This would not be so bad, were it not for the fact that Prince Baka is a trickster with little restraint, while his minders are trying to keep any interstellar incident from being set off.&lt;/p&gt;
&lt;p&gt;Thus kicks off the case of the long-term enemy race who are serious baseball fans, the random highschoolers who are turned into a &lt;span style="font-style:italic;"&gt;sentai&lt;/span&gt; team (and their alien assassin teacher), the hive-queen who comes to Earth looking for a mate (and finds something other than she'd expected), the last of the twin-tailed mermaids, and finally Baka's betrothed and brother who are determined to see him wed.  Baka sails through all this in total serenity, while his minders sink further into helpless rage.&lt;/p&gt;
&lt;p&gt;Adapted from a 15 year old manga, it stuck out from all the other titles on offer for the first quarter.&lt;/p&gt;

&lt;h4&gt;Currently watching&lt;/h4&gt;
&lt;p&gt;&lt;span style="font-style:italic;"&gt;Mobile Suit Gundam&lt;/span&gt; -- and that's it.&lt;/p&gt;
&lt;p&gt;The only one of the current season's offerings I might have tried (&lt;span style="font-style:italic;"&gt;Hyouge Mono&lt;/span&gt;) isn't on Crunchyroll, and even were I to go back into the grey world of fansubs, it's a title that has all the hallmarks of a series that will never get fully subbed for years.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-4518288158029327925?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/4518288158029327925/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=4518288158029327925' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/4518288158029327925'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/4518288158029327925'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/05/belated-reviews-anime.html' title='Belated Reviews &amp;#8212; Anime'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-iTnH_obbUeg/Td_y2do8pEI/AAAAAAAABBQ/7B7QeIgU2RU/s72-c/otome-yokai-zakuro-screen.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-291714653611332771</id><published>2011-05-24T21:07:00.001+01:00</published><updated>2011-05-27T11:51:02.640+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='cycling'/><category scheme='http://www.blogger.com/atom/ns#' term='don&apos;t talk to me about life'/><title type='text'>Spring Cycling 2011</title><content type='html'>&lt;p&gt;A short holiday in a spell of warm, bright but very windy weather.  I started by doing a quick spin out to lunch at the Crown and Punchbowl on Saturday, returning along the towpath; then set out to re-do one of the short tours that I did &lt;a href="http://stevegilham.blogspot.com/2009/07/summer-cycling.html"&gt;a couple of years ago&lt;/a&gt;&lt;/p&gt;
&lt;iframe width="425" height="450" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="http://maps.google.co.uk/maps/ms?ie=UTF8&amp;amp;hl=en&amp;amp;msa=0&amp;amp;msid=214190258906006153051.0004a40d0e1a1e89cd805&amp;amp;ll=52.251346,1.163177&amp;amp;spn=0.378328,0.583649&amp;amp;z=10&amp;amp;output=embed"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;small&gt;View &lt;a href="http://maps.google.co.uk/maps/ms?ie=UTF8&amp;amp;hl=en&amp;amp;msa=0&amp;amp;msid=214190258906006153051.0004a40d0e1a1e89cd805&amp;amp;ll=52.251346,1.163177&amp;amp;spn=0.378328,0.583649&amp;amp;z=10&amp;amp;source=embed" style="color:#0000FF;text-align:left"&gt;Spring 2011&lt;/a&gt; in a larger map&lt;/small&gt;
&lt;p&gt;There was a sprinkling of rain on the Sunday lunchtime, and again overnight on Monday, but that was really of no consequence, unlike the previous time.  The wind, however, was a different story, making the going a slow grind, and the journeys not so full of detours as before.&lt;/p&gt;
&lt;p&gt;The minor road into Stowmarket was closed, as I discovered on the way in; so I tried to follow the intended route -- only to miss a turn and only realise on reaching the A140, so looped back to the intended route after crossing.&lt;/p&gt;
&lt;p&gt;So, by the time I got to Cotton, it was about opening time -- and thus started a litany of non-functioning pubs, ending up at the Waterfront Inn in Diss where I could get food as well as drink in mid-afternoon.  Just as well I had a substantial meal, as it turned out that the kitchens at the Scole Inn close on Sunday evenings.&lt;/p&gt;
&lt;p&gt;The next morning, looking at the time and weather, I made the journey to Framlingham, stopping at Stradbroke to pick up some juice and biscuits, and them pausing on convenient benches to rest and read a while, arriving in time for a late lunch, and plenty of time to amble around the castle.
&lt;div class="center" style="min-width:260px;border: 1px solid silver;padding:5px;margin:5px;"&gt;
&lt;a href="http://www.flickr.com/photos/stevegilham/5756006412/" title="Castle from Gatehouse by Steve Gilham, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5065/5756006412_d95da20917.jpg" width="500" height="375" alt="Castle from Gatehouse"&gt;&lt;/a&gt;
&lt;p&gt;Castle from Gatehouse&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The weather continuing windy, the massive detour I did the previous time was out of the question, so instead I stopped at Sutton Hoo, doing all the woodland walks, as well as the exhibits; and rehydrating with coffee and soup.&lt;/p&gt;
&lt;div class="center" style="min-width:260px;border: 1px solid silver;padding:5px;margin:5px;"&gt;
&lt;a href="http://www.flickr.com/photos/stevegilham/5756077776/" title="The GAR seat by Steve Gilham, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5227/5756077776_ddde51becf.jpg" width="500" height="375" alt="The GAR seat"&gt;&lt;/a&gt;&lt;p&gt;The GAR seat at Sutton Hoo&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;One final stop at the Dog at Grundisburgh, then the final leg into the wind; and the official return route using the cycle crossing for the A140, rather than my more usual approach from other directions.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-291714653611332771?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/291714653611332771/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=291714653611332771' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/291714653611332771'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/291714653611332771'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/05/spring-cycling-2011.html' title='Spring Cycling 2011'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://farm6.static.flickr.com/5065/5756006412_d95da20917_t.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-7739670599274197245</id><published>2011-05-20T08:07:00.002+01:00</published><updated>2011-05-20T08:41:47.065+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>CodeDOM -- generating an interface to a type</title><content type='html'>&lt;p&gt;This is a fairly simple process -- reflect over the public properties and methods, and generate a matching interface member, rather like this:&lt;/p&gt;
&lt;pre class="brush: f#"&gt;let GenerateInterface (target:Type) =
  let compileUnit = new CodeCompileUnit()
  
  let scope = new CodeNamespace("Generated")
  
  let namespaces = new HashSet&amp;lt;string&amp;gt;() 
  
  compileUnit.Namespaces.Add( scope ) |&amp;gt; ignore
  let wrapper = new CodeTypeDeclaration("I" + target.Name + "Wrapper")
  wrapper.IsInterface &amp;lt;- true
  scope.Types.Add(wrapper) |&amp;gt; ignore
  IncludeProperties wrapper target namespaces
  IncludeMethods wrapper target namespaces
       
  namespaces
  |&amp;gt; Seq.sort
  |&amp;gt; Seq.iter (fun n-&amp;gt; scope.Imports.Add(new CodeNamespaceImport(n)))
  compileUnit&lt;/pre&gt;
&lt;p&gt;which puts the required namespaces into order (TODO: sort &lt;code&gt;System.*&lt;/code&gt; ahead of the rest).&lt;/p&gt;
&lt;p&gt;The two functions look much the same, so could be factored further:&lt;/p&gt;
&lt;pre class="brush: f#"&gt;let IncludeProperties (klass:CodeTypeDeclaration) (target:Type) (namespaces:HashSet&amp;lt;string&amp;gt;) =
  target.GetProperties(BindingFlags.Instance ||| BindingFlags.Public)
  |&amp;gt; Seq.sortBy (fun p -&amp;gt; p.Name)
  |&amp;gt; Seq.iter (fun p -&amp;gt;
                   let m = new CodeMemberProperty()
                   m.Name &amp;lt;- p.Name
                   namespaces.Add(p.PropertyType.Namespace) |&amp;gt; ignore
                   m.Type &amp;lt;- TypeRef(p.PropertyType)
                   m.HasGet &amp;lt;- p.CanRead
                   m.HasSet &amp;lt;- p.CanWrite
                   p.GetIndexParameters() 
                   |&amp;gt; Seq.iter ( fun t -&amp;gt; 
                                 let px = new CodeParameterDeclarationExpression()
                                 px.Direction &amp;lt;- FieldDirection.In
                                 px.Name &amp;lt;- t.Name
                                 namespaces.Add(t.ParameterType.Namespace) |&amp;gt; ignore
                                 px.Type &amp;lt;- TypeRef(t.ParameterType)
                                 m.Parameters.Add(px) |&amp;gt; ignore
                                )
                   if m.Parameters.Count &amp;gt; 0 then m.Name &amp;lt;- "Item"
                   klass.Members.Add( m ) |&amp;gt; ignore
              )
&lt;/pre&gt;
&lt;p&gt;which &lt;a href="http://msdn.microsoft.com/en-us/library/system.codedom.codememberproperty.parameters.aspx"&gt;special cases the indexer property&lt;/a&gt;; and &lt;/p&gt;
&lt;pre class="brush: f#"&gt;let IncludeMethods (klass:CodeTypeDeclaration) (target:Type) (namespaces:HashSet&amp;lt;string&amp;gt;) =
  target.GetMethods(BindingFlags.Instance ||| BindingFlags.Public)
  |&amp;gt; Seq.filter (fun x -&amp;gt; not x.IsSpecialName )
  |&amp;gt; Seq.sortBy (fun p -&amp;gt; p.Name)
  |&amp;gt; Seq.iter (fun p -&amp;gt;
                   let m = new CodeMemberMethod()
                   m.Name &amp;lt;- p.Name
                   m.ReturnType &amp;lt;- TypeRef(p.ReturnType)
                   namespaces.Add(p.ReturnType.Namespace) |&amp;gt; ignore
                   p.GetParameters()
                   |&amp;gt; Seq.iter ( fun (t:ParameterInfo) -&amp;gt; 
                                 let px = new CodeParameterDeclarationExpression()
                                 px.Direction &amp;lt;- if t.IsIn &amp;&amp; t.IsOut then FieldDirection.Ref
                                                 else if t.IsOut then FieldDirection.Out 
                                                 else FieldDirection.In
                                 px.Name &amp;lt;- t.Name
                                 namespaces.Add(t.ParameterType.Namespace) |&amp;gt; ignore
                                 px.Type &amp;lt;- TypeRef(t.ParameterType)
                                 m.Parameters.Add(px) |&amp;gt; ignore
                                )
                   klass.Members.Add( m ) |&amp;gt; ignore
              )&lt;/pre&gt;
&lt;p&gt;(TODO: generics and attributes, which I don't need for my immediate use case)&lt;/p&gt;
&lt;p&gt;To make the code look less cluttered, to get type specifications which aren't fully namespace qualified everywhere, we want to specify types with keyword synonyms by type object, and the rest by name (with the namespaces included by &lt;code&gt;using&lt;/code&gt; or &lt;code&gt;open&lt;/code&gt;).  So we have helper functions&lt;/p&gt;
&lt;pre class="brush: f#"&gt;let SysType (t:Type) =
  if t = typeof&amp;lt;System.Object&amp;gt; ||
     t = typeof&amp;lt;System.Char&amp;gt; ||
     t = typeof&amp;lt;System.Boolean&amp;gt; ||
     t = typeof&amp;lt;System.String&amp;gt; ||
     t = typeof&amp;lt;System.Int32&amp;gt; ||
  // also uint, float, etc...  ; probably also Nullables
     t = typeof&amp;lt;System.Void&amp;gt; then true
  else false
    
let TypeRef (t:Type) =
  if t.IsArray then
    if SysType &amp;lt;| t.GetElementType() then new CodeTypeReference(t)
    else new CodeTypeReference(t.Name)
  else if SysType t then new CodeTypeReference(t)
  else new CodeTypeReference(t.Name)&lt;/pre&gt;
&lt;p&gt;where again, TODO handling generic types, especially collections, to get them looking pretty.&lt;/p&gt; 
&lt;p&gt;So if we use &lt;code&gt;string&lt;/code&gt; as the type, and dump to C# we get&lt;/p&gt;
&lt;pre class="brush: c#"&gt;namespace Generated {
    using System;
    using System.Globalization;
    using System.Text;
    
    
    public interface IStringWrapper {
        
        char this[int index] {
            get;
        }
        
        int Length {
            get;
        }
        
        object Clone();

        ...
        
        string TrimStart(char[] trimChars);
    }
}&lt;/pre&gt;
&lt;p&gt;and in F#&lt;/p&gt;
&lt;pre class="brush: f#"&gt;namespace Generated
    // Generated by F# CodeDom stuff omitted...
    open System
    open System.Globalization
    open System.Text
    
    type
        IStringWrapper = interface
            abstract Item : int -&amp;gt; char with get
            
            abstract Length : int with get
            
            abstract TrimStart : char[] -&amp;gt; string
            
            ...
            
            abstract Clone : unit -&amp;gt; obj
        end&lt;/pre&gt;
&lt;p&gt;where interestingly, the sorted order of the inserted members is reversed.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-7739670599274197245?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/7739670599274197245/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=7739670599274197245' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/7739670599274197245'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/7739670599274197245'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/05/codedom-generating-interface-to-type.html' title='CodeDOM -- generating an interface to a type'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-218068312254718066</id><published>2011-05-16T21:44:00.004+01:00</published><updated>2011-05-16T22:11:59.139+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Hello CodeDOM -- building C# and F# code from F#</title><content type='html'>&lt;p&gt;A five-finger exercise in CodeDOM &lt;a href="http://msdn.microsoft.com/en-us/library/saf5ce06.aspx#Y32"&gt;using the "Hello World" example&lt;/a&gt; from F# -- more interesting stuff to follow later:&lt;/p&gt;
&lt;pre class="brush: f#"&gt;module ProofOfConcept

open System.CodeDom
open System.CodeDom.Compiler
open System.IO
open Microsoft.CSharp
open Microsoft.FSharp.Compiler.CodeDom

let GenerateCode(compileunit: CodeCompileUnit) (provider:CodeDomProvider) =
    // Build the output file name.
    let sourceFile = Path.ChangeExtension("HelloWorld.", provider.FileExtension)

    // Create a TextWriter to a StreamWriter to the output file.
    use sw = new StreamWriter(sourceFile, false)
    let tw = new IndentedTextWriter(sw, "  ")

    // Generate source code using the code provider.
    provider.GenerateCodeFromCompileUnit(compileunit, tw,
            new CodeGeneratorOptions())
    tw.Close()
    sourceFile

let GenerateCSharpCode(compileunit: CodeCompileUnit) =
    // Generate the code with the C# code provider.
    let provider = new CSharpCodeProvider();
    GenerateCode compileunit provider
    
let GenerateFSharpCode(compileunit: CodeCompileUnit) =
    // Generate the code with the F# code provider.
    let provider = new FSharpCodeProvider();
    GenerateCode compileunit provider

[&amp;lt;EntryPoint&amp;gt;]
let main a =
  let compileUnit = new CodeCompileUnit()
  let samples = new CodeNamespace("Samples")
  samples.Imports.Add(new CodeNamespaceImport("System"))
  compileUnit.Namespaces.Add( samples ) |&amp;gt; ignore
  let class1 = new CodeTypeDeclaration("Class1")
  samples.Types.Add(class1) |&amp;gt; ignore
  let start = new CodeEntryPointMethod();
  let cs1 = new CodeMethodInvokeExpression(
                    new CodeTypeReferenceExpression("System.Console"),
                    "WriteLine", new CodePrimitiveExpression("Hello CodeDOM World!"))
  start.Statements.Add(cs1) |&amp;gt; ignore
  class1.Members.Add( start ) |&amp;gt; ignore
  GenerateCSharpCode compileUnit |&amp;gt; ignore
  GenerateFSharpCode compileUnit |&amp;gt; ignore
  0&lt;/pre&gt;
&lt;p&gt;which yields up&lt;/p&gt;
&lt;pre class="brush: c#"&gt;//------------------------------------------------------------------------------
// &amp;lt;auto-generated&amp;gt;
//     This code was generated by a tool.
//     Runtime Version:2.0.50727.4211
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// &amp;lt;/auto-generated&amp;gt;
//------------------------------------------------------------------------------

namespace Samples {
    using System;
    
    
    public class Class1 {
        
        public static void Main() {
            System.Console.WriteLine("Hello CodeDOM World!");
        }
    }
}&lt;/pre&gt;
&lt;p&gt;which isn't StyleCop compliant, but is otherwise well formatted; and&lt;/p&gt;
&lt;pre class="brush: f#"&gt;
//------------------------------------------------------------------------------
// &amp;lt;autogenerated&amp;gt;
//     This code was generated by a tool.
//     Runtime Version: 2.0.50727.4211
//
//     Changes to this file may cause incorrect behavior and will be lost if 
//     the code is regenerated.
// &amp;lt;/autogenerated&amp;gt;
//------------------------------------------------------------------------------

namespace global

namespace Samples
    // Generated by F# CodeDom
    #nowarn "49" // uppercase argument names
    #nowarn "67" // this type test or downcast will always hold
    #nowarn "66" // this upcast is unnecessary - the types are identical
    #nowarn "58" // possible incorrect indentation..
    #nowarn "57" // do not use create_DelegateEvent
    #nowarn "51" // address-of operator can occur in the code
    #nowarn "1183" // unused 'this' reference
    open System
    
    exception ReturnException2ea1f686ef3e4730bedf29e248ff4aab of obj
    exception ReturnNoneException2ea1f686ef3e4730bedf29e248ff4aab
    [&amp;lt;AutoOpen&amp;gt;]
    module FuncConvertFinalOverload2ea1f686ef3e4730bedf29e248ff4aab =
      // This extension member adds to the FuncConvert type and is the last resort member in the method overloading rules. 
      type global.Microsoft.FSharp.Core.FuncConvert with
          /// A utility function to convert function values from tupled to curried form
          static member FuncFromTupled (f:'T -&amp;gt; 'Res) = f
    
    type
        
        Class1 = class
            new() as this =
                {
                }
            static member UnnamedMethod_0  () =
                System.Console.WriteLine("Hello CodeDOM World!") |&amp;gt; ignore
        end
    [&amp;lt;CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)&amp;gt;]
    module __EntryPoint =
        [&amp;lt;EntryPoint&amp;gt;]
        let Main (args:string[]) =
            Class1.UnnamedMethod_0()
            0&lt;/pre&gt;
&lt;p&gt;which is a little idiosyncratic; the GUID-named types seem to be there in case of need from some construct that is not actually used in this simple example.&lt;/p&gt;
&lt;p&gt;Now it just comes down to assembling to code object graph for something practical.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-218068312254718066?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/218068312254718066/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=218068312254718066' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/218068312254718066'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/218068312254718066'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/05/hello-codedom-building-c-and-f-code.html' title='Hello CodeDOM -- building C# and F# code from F#'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-1445218490846529824</id><published>2011-05-15T11:40:00.005+01:00</published><updated>2011-05-15T12:16:25.821+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><title type='text'>Not sure if bug...</title><content type='html'>&lt;p&gt;...but I set it to the fsbugs e-mail anyway.&lt;/p&gt;
&lt;p&gt;Let's start with this F# code:&lt;/p&gt;
&lt;pre class="brush: f#"&gt;module Test.Component

// After the Haskell type
type public Either&amp;lt;'TFault, 'TSuccess&amp;gt; =
  | Left of 'TFault 
  | Right of 'TSuccess
  with 
   [&amp;lt;CompiledName("ForEvery")&amp;gt;]
   static member iter (fault : 'TFault -&amp;gt; unit) (fix : 'TSuccess -&amp;gt; unit) (value : Either&amp;lt;'TFault, 'TSuccess&amp;gt;) =
     match value with
     | Left a -&amp;gt; fault a
     | Right b -&amp;gt; fix b

   [&amp;lt;CompiledName("ForEach")&amp;gt;]
   static member iter0 = 
     Either&amp;lt;'TFault, 'TSuccess&amp;gt;.iter ignore
  
   [&amp;lt;CompiledName("ToOption")&amp;gt;]
   static member toOption (value:Either&amp;lt;'TFault, 'TSuccess&amp;gt;) = 
     match value with
     | Right y -&amp;gt; Some y
     | _ -&amp;gt; None 
end &lt;/pre&gt;
&lt;p&gt;The last function maps to C# as&lt;/p&gt;
&lt;pre class="brush: c#"&gt;namespace Test
{
 [CompilationMapping(SourceConstructFlags.Module)]
 public static class Component
 {
  [DebuggerDisplay("{__DebugDisplay(),nq}"), CompilationMapping(SourceConstructFlags.SumType)]
  [Serializable]
  [StructLayout(LayoutKind.Auto, CharSet = CharSet.Auto)]
  public abstract class Either&amp;lt;TFault, TSuccess&amp;gt; : IEquatable&amp;lt;Component.Either&amp;lt;TFault, TSuccess&amp;gt;&amp;gt;, IStructuralEquatable, IComparable&amp;lt;Component.Either&amp;lt;TFault, TSuccess&amp;gt;&amp;gt;, IComparable, IStructuralComparable
  {
...
   [CompilationSourceName("toOption")]
   public static FSharpOption&amp;lt;TSuccess&amp;gt; ToOption(Component.Either&amp;lt;TFault, TSuccess&amp;gt; value)
   {
    if (value is Component.Either&amp;lt;TFault, TSuccess&amp;gt;.Right)
    {
     Component.Either&amp;lt;TFault, TSuccess&amp;gt;.Right right = (Component.Either&amp;lt;TFault, TSuccess&amp;gt;.Right)value;
     TSuccess y = right.item;
     return FSharpOption&amp;lt;TSuccess&amp;gt;.Some(y);
    }
    return null;
   }
...
    }
  }
}  &lt;/pre&gt;
&lt;p&gt;Now change the initial F# line to be &lt;code&gt;namespace&lt;/code&gt; rather than &lt;code&gt;module&lt;/code&gt;...&lt;/p&gt;
&lt;pre class="code"&gt;File1.fs(9,18): error FS0755: The 'CompiledName' attribute cannot be used with this language element
File1.fs(15,18): error FS0755: The 'CompiledName' attribute cannot be used with this language element
File1.fs(19,18): error FS0755: The 'CompiledName' attribute cannot be used with this language element&lt;/pre&gt;
&lt;p&gt;So for comparison purposes, let's replace &lt;code&gt;CompiledName&lt;/code&gt; with &lt;code&gt;Obsolete&lt;/code&gt;, and see what we've built...&lt;/p&gt;
&lt;pre class="brush: c#"&gt;namespace Test.Component
{
 [DebuggerDisplay("{__DebugDisplay(),nq}"), CompilationMapping(SourceConstructFlags.SumType)]
 [Serializable]
 [StructLayout(LayoutKind.Auto, CharSet = CharSet.Auto)]
 public abstract class Either&amp;lt;TFault, TSuccess&amp;gt; : IEquatable&amp;lt;Either&amp;lt;TFault, TSuccess&amp;gt;&amp;gt;, IStructuralEquatable, IComparable&amp;lt;Either&amp;lt;TFault, TSuccess&amp;gt;&amp;gt;, IComparable, IStructuralComparable
 {
...
  [Obsolete("ToOption")]
  public static FSharpOption&amp;lt;TSuccess&amp;gt; toOption(Either&amp;lt;TFault, TSuccess&amp;gt; value)
  {
   if (value is Either&amp;lt;TFault, TSuccess&amp;gt;.Right)
   {
    Either&amp;lt;TFault, TSuccess&amp;gt;.Right right = (Either&amp;lt;TFault, TSuccess&amp;gt;.Right)value;
    TSuccess y = right.item;
    return FSharpOption&amp;lt;TSuccess&amp;gt;.Some(y);
   }
   return null;
  }
...
  }
}&lt;/pre&gt;
&lt;p&gt;where I can't for the life of me see the difference.&lt;/p&gt;
&lt;p&gt;This happens when building using the latest compiler release in either VS2008 shell or VS2010 shell; or from the command line using MSBuild for either .net3.5 or 4.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-1445218490846529824?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/1445218490846529824/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=1445218490846529824' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/1445218490846529824'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/1445218490846529824'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/05/not-sure-if-bug.html' title='Not sure if bug...'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-6161402745429195455</id><published>2011-05-06T06:52:00.002+01:00</published><updated>2011-05-06T06:56:00.095+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Vista'/><category scheme='http://www.blogger.com/atom/ns#' term='PowerShell'/><title type='text'>Fixing missing volume and network tray icons</title><content type='html'>&lt;p&gt;On some Vista systems, there's an intermittent corruption of the icons that give network status and volume control in the system notification "tray".&lt;/p&gt;
&lt;p&gt;Running this PowerShell script will clear the wedged icons and restart explorer&lt;/p&gt;
&lt;pre class="code"&gt;remove-itemproperty -path "hkcu:\Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\TrayNotify" -
name IconsStream
remove-itemproperty -path "hkcu:\Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\TrayNotify" -
name PastIconsStream
$explorer = get-process -Name explorer
$path = $explorer.Path
stop-process -Name explorer
start-process $path&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-6161402745429195455?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/6161402745429195455/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=6161402745429195455' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/6161402745429195455'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/6161402745429195455'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/05/fixing-missing-volume-and-network-tray.html' title='Fixing missing volume and network tray icons'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-7026570372371287465</id><published>2011-04-30T23:58:00.003+01:00</published><updated>2011-05-25T09:52:19.764+01:00</updated><title type='text'>Graduation day</title><content type='html'>&lt;div class="center" style="min-width:260px;border: 1px solid silver;padding:5px;margin:5px;"&gt;
&lt;a href="http://www.flickr.com/photos/stevegilham/5676273918/" title="Graduation Day by Steve Gilham, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5023/5676273918_7f3eb46d70.jpg" width="500" height="375" alt="Graduation Day"&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;So, 32 years after taking Part III of the Maths Tripos, I've finally received a degree for it, the MMath, the most junior of the three Masters degrees I hold from Cambridge.&lt;/p&gt;
&lt;p&gt;With this batch of retrospective degrees, my old college -- Emmanuel -- wanted to make big celebration of it, so as well as going to be admitted to the degree in person, it was an occasion to take up, for the very first time, my MA dining rights.  And with such a retrospective grant there were people ranging from retirees to those who had still to take the MA -- but comparatively few who had actually done much mathematical for long after Part III!  Stranger still to see some people for the first time in half a lifetime or more -- "Did I used to supervise you?"&lt;/p&gt;
&lt;p&gt;The weather was bright and rather breezy, though the Chapel Court, where the photo was taken (if only I'd noticed I'd still not untucked the bands from under the hood), was quite still, so while waiting for the reception before lunch we waited in the shade in New Court -- admiring the roses and irises already blooming in the sheltered and sunny parts.  And it was at that point that I realised that the stitching had failed down the side of both of my new pair of shoes...&lt;/p&gt;
&lt;p&gt;There was discreet disabled access into the Old Library for the drinks, and then, it Hall, Karen was seated at the end of one table, avoiding any problems with the benches.&lt;/p&gt;
&lt;p&gt;After lunch we separated as I went to get placed into the correct order in the crocodile, have the ceremony rehearsed, and then marched to the Senate House. Once inside it was possible to discreetly dab at the perspiration from a brisk walk in the sun in all the gear -- graduation days are usually either hot or wet -- while waiting our turn.&lt;/p&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/-zrdbsMDcCk4/TdzDLawG0mI/AAAAAAAABBE/F3IrBjngBuo/s1600/mmath.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 255px; height: 320px;" src="http://2.bp.blogspot.com/-zrdbsMDcCk4/TdzDLawG0mI/AAAAAAAABBE/F3IrBjngBuo/s320/mmath.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5610573836723868258" /&gt;&lt;/a&gt;
&lt;p&gt;After having been processed, without tripping over my gown, or losing bits of shoe, time to go out onto the lawn, get some professional photos, chat with other people I knew also getting the MMath, before heading off, handing back the hood and gown (Karen was wearing our joint MA gown, as a Member of the University attending a Congregation), and then, at last, taking off the shoes that were just about holding upper and soles together, to go barefoot back to the car.&lt;/p&gt;
&lt;p&gt;After a short pause to recover, I then cycled back into town for Hall dominated by newly minted MMath graduates; a very pleasant meal followed by cheese, fruit and chocolates with coffee port and Madeira in the parlour, all with a great deal of reminiscence.&lt;/p&gt;
&lt;p&gt;And then, about half past ten, back on the bike to cycle back home in the pleasantly cool evening, and with surprisingly little traffic on any of the roads.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-7026570372371287465?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/7026570372371287465/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=7026570372371287465' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/7026570372371287465'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/7026570372371287465'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/04/graduation-day.html' title='Graduation day'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://farm6.static.flickr.com/5023/5676273918_7f3eb46d70_t.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-8892829008040579141</id><published>2011-04-15T22:05:00.007+01:00</published><updated>2011-04-20T23:02:24.084+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><title type='text'>Applying IEnumerable to chunked IEnumerable in F# -- Cipher Feedback mode</title><content type='html'>&lt;p&gt;&lt;a href="http://stevegilham.blogspot.com/2011/04/generalized-ienumerable-to-chunked_12.html"&gt;Having developed the supporting infrastructure&lt;/a&gt; to pull data from any &lt;code&gt;IEnumerable&lt;/code&gt; into a sequence fixed length buckets, we can now apply it to some practical use.  One such is to perform encryption of an input byte sequence in a block-cipher feeback (CFB) mode.&lt;/p&gt;
&lt;p&gt;CFB mode is so simple, it almost feels like cheating.  Given an initialization vector (IV) -- an arbitrary block of bytes -- as the 0-th ciphertext block, the iterative definition is that the n-th ciphertext block is the encrypted value of the n-1-th block XOR'd with the n-th plaintext block; and by the symmetry of XOR, the n-th plaintext block is recovered from the n-th ciphertext block by the same XOR.&lt;/p&gt;
&lt;p&gt;So, let's implement it, and do a simple test that we can easily work out the cipherstream for -- and doesn't actually involve real encryption.  So let's take 128-bit blocks, all zeros for the IV, and the identity transformation for our "encryption", and have the integers 0 to 41 as input.&lt;/p&gt;
&lt;p&gt;The first ciphertext block will be all zeroes (IV encrypted) XOR'd with 0-15, i.e. 0-15.  That XORs with 16-31 to give a second block of all 16s; the final block is 32-41 XORd with 16s, i.e. 48-57.&lt;/p&gt;
&lt;p&gt;So, armed with expectations we can write the implementation with appropriate self-test code, thus -- &lt;/p&gt;
&lt;pre class="brush: f#"&gt;namespace Tinesware.Enumerables

open System
open System.Collections.Generic
open System.IO
open System.Linq
open Should.Fluent

// An internal type to wrap an IEnumerator&amp;lt;'a&amp;gt;
// and disallow arbitrary disposal.
type private Controlled&amp;lt;'a&amp;gt;(iterator: IEnumerator&amp;lt;'a&amp;gt;) =
    interface IEnumerator&amp;lt;'a&amp;gt; with
      member self.Current = iterator.Current
    interface System.Collections.IEnumerator with
      member self.Current = iterator.Current :&amp;gt; Object
      member self.Reset() = iterator.Reset()
      member self.MoveNext() = iterator.MoveNext()
    interface IDisposable with
      member self.Dispose() = () // The whole purpose of the type -- prevent premature disposal

// Changes the semantics of any sequence to be streamlike
// There is just the one iterator which marches forwards
// inexorably, whatever the underlying sequence
type StreamingSequence&amp;lt;'a&amp;gt;(source: IEnumerable&amp;lt;'a&amp;gt;) =
    let enumerator = source.GetEnumerator()
    let iterator = new Controlled&amp;lt;'a&amp;gt;(enumerator) :&amp;gt; IEnumerator&amp;lt;'a&amp;gt;
    member self.Release() = enumerator.Dispose()
    interface IEnumerable&amp;lt;'a&amp;gt; with
        member self.GetEnumerator() = iterator
    interface System.Collections.IEnumerable with
        member self.GetEnumerator() = iterator :&amp;gt; System.Collections.IEnumerator
        
module Chunk =
   
    /// &amp;lt;summary&amp;gt;
    /// Split an enumerable into an enumeration of arrays
    /// &amp;lt;/summary&amp;gt;
    /// &amp;lt;typeparam name="T"&amp;gt;Element type of the array&amp;lt;/typeparam&amp;gt;
    /// &amp;lt;param name="source"&amp;gt;The IEnumerable to operate on&amp;lt;/param&amp;gt;
    /// &amp;lt;param name="chunk"&amp;gt;The window size&amp;lt;/param&amp;gt;
    /// &amp;lt;returns&amp;gt;The enumeration&amp;lt;/returns&amp;gt;
    let Window (chunk : int) (source : 'a seq) =
        let rec innerWindowing (x :StreamingSequence&amp;lt;'a&amp;gt;) =
           seq {
               let result = x |&amp;gt; Seq.truncate chunk |&amp;gt; Seq.toArray
               if result.Length &amp;gt; 0 then
                 yield result
                 yield! innerWindowing x
               else
                 x.Release()
               }
        
        // StreamingSequence makes the truncate/skip behaviour
        // predictable, whatever the semantics of the 
        // underlying sequence provider
        innerWindowing &amp;lt;| new StreamingSequence&amp;lt;'a&amp;gt;(source)
        
    // Exclusive-or two byte sequences to the 
    // length of the shorter; returns an array
    let (^) (a: byte seq) (b: byte seq) =
      Seq.zip a b
      |&amp;gt; Seq.map (fun x -&amp;gt; (fst x ^^^ snd x))
      |&amp;gt; Seq.toArray
        
    // Produce a function that performs cfb-mode enciphering
    // given the block cipher forward encryption and an IV
    // Alas the type system doesn't permit us to say that all
    // these byte arrays must be the same size -- that would
    // take C++, I fear
    let CfbEncrypt (encrypt : byte[] -&amp;gt; byte[]) (iv : byte[])  =
      let cn_1 = ref iv
      let operation (plaintext : byte[]) = 
        let ecn_1 = encrypt (!cn_1)
        let cn = ecn_1 ^ plaintext
        cn_1 := cn
        cn
      operation
      
    /// &amp;lt;summary&amp;gt;
    /// A Self-test program for CFB mode
    /// &amp;lt;/summary&amp;gt;
    [&amp;lt;EntryPoint&amp;gt;]
    let main a =
        let input = seq { 0 .. 41 }
                    |&amp;gt; Seq.map byte
                    |&amp;gt; Seq.toArray
                    
        let iv = seq { 1 .. 16 }
                 |&amp;gt; Seq.map (fun x -&amp;gt; 0uy)
                 |&amp;gt; Seq.toArray

        let cfb = CfbEncrypt id iv
        let output = input 
                        |&amp;gt; Window 16
                        |&amp;gt; Seq.map cfb
                        |&amp;gt; Seq.collect id // unWindow
                        |&amp;gt; Seq.toArray
                        
        let expected = [| 0uy;  1uy;  2uy;  3uy;  4uy;  5uy;  6uy;  7uy; 
                          8uy;  9uy; 10uy; 11uy; 12uy; 13uy; 14uy; 15uy; // zeros xor 0-15
                         16uy; 16uy; 16uy; 16uy; 16uy; 16uy; 16uy; 16uy; 
                         16uy; 16uy; 16uy; 16uy; 16uy; 16uy; 16uy; 16uy; // 0-15 xor 16-31
                         48uy; 49uy; 50uy; 51uy; 52uy; 53uy; 54uy; 55uy;
                         56uy; 57uy; |]                                  //16s xor 32-41
                         
        output.Length.Should().Equal(input.Length) |&amp;gt; ignore
        let matching = Seq.forall2 (fun x y -&amp;gt; x = y) expected output 
        matching.Should().Be.True |&amp;gt; ignore
        
        // new instance to reset the IV
        let cfb = CfbEncrypt id iv

        let decrypt = output
                        |&amp;gt; Window 16
                        |&amp;gt; Seq.map cfb
                        |&amp;gt; Seq.collect id // unWindow
                        |&amp;gt; Seq.toArray
        
        decrypt.Length.Should().Equal(input.Length) |&amp;gt; ignore
        let matching = Seq.forall2 (fun x y -&amp;gt; x = y) input decrypt 
        matching.Should().Be.True |&amp;gt; ignore

        0&lt;/pre&gt;
&lt;p&gt;where I've renamed the previous private &lt;code&gt;Ratchet&lt;/code&gt; class to the public &lt;code&gt;StreamingSequence&lt;/code&gt; type -- a more self-explanatory name for possible re-users; and exposed the &lt;code&gt;Dispose&lt;/code&gt; method of the constant (reference, mutable internal state) &lt;code&gt;IEnumerator&lt;/code&gt; for end-of-enumeration use.&lt;/p&gt;
&lt;p&gt;Here, &lt;code&gt;Seq.collect&lt;/code&gt; is being used to flatten the sequence after the block-level operation has been done.&lt;/p&gt;
&lt;p&gt;CFB mode is so simple that -- having already done the hard work of breaking any sequence into blocks -- the only thing that stopped the test running through first time was forgetting to re-initialize the CFB operation for the decryption pass.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Actually, the decryption operation shouldn't be working -- but somehow it does, even when I push in random input, and replace the identity encryption operation with a one-way hash!  Something weird is going on here.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-8892829008040579141?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/8892829008040579141/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=8892829008040579141' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/8892829008040579141'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/8892829008040579141'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/04/applying-ienumerable-to-chunked.html' title='Applying IEnumerable to chunked IEnumerable in F# -- Cipher Feedback mode'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-8698405704995073359</id><published>2011-04-13T07:56:00.004+01:00</published><updated>2011-05-29T22:07:20.218+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><title type='text'>F# under the covers XIII -- Sequence expressions</title><content type='html'>&lt;p&gt;After more than a year since &lt;a href="http://stevegilham.blogspot.com/2010/02/f-under-covers-xii-reflector-60.html"&gt;the previous instalment&lt;/a&gt;...&lt;/p&gt;
&lt;p&gt;A C# method that returns an enumerator via &lt;code&gt;yield return&lt;/code&gt; generates a synthetic class that looks like this:&lt;/p&gt;
&lt;pre class="brush: c#"&gt;private sealed class &amp;lt;ToEnumerable&amp;gt;d__0 : IEnumerable&amp;lt;T&amp;gt;, IEnumerable, IEnumerator&amp;lt;T&amp;gt;, IEnumerator, IDisposable
{
    // data fields omitted
    private T System.Collections.Generic.IEnumerator&amp;lt;System.T&amp;gt;.Current { get; }
    private object System.Collections.IEnumerator.Current { get; }
    public &amp;lt;ToEnumerable&amp;gt;d__0(int &amp;lt;&amp;gt;1__state) : base() { ... }
    private IEnumerator&amp;lt;T&amp;gt; System.Collections.Generic.IEnumerable&amp;lt;System.T&amp;gt;.GetEnumerator() { ... }
    private IEnumerator System.Collections.IEnumerable.GetEnumerator(){ ... }
    private bool MoveNext() { ... }

    private void System.Collections.IEnumerator.Reset()
    {
        throw new NotSupportedException();
    }
    private void System.IDisposable.Dispose()
    {
    }
}&lt;/pre&gt;
&lt;p&gt;where I omit unimportant detail based on the particular enumeration, but draw attention to how the other &lt;code&gt;IEnumerator&lt;/code&gt; methods are implemented.&lt;/p&gt;
&lt;p&gt;The F# result from a sequence expression is very different, with an explicit close and dispose:&lt;/p&gt;
&lt;pre class="brush: c#"&gt;internal sealed class ToEnumerable@18 : GeneratedSequenceBase&amp;lt;T&amp;gt;
{
    // data fields omitted
    public ToEnumerable@18(Stream stream, int result, T v, IEnumerator&amp;lt;T&amp;gt; @enum, int pc, T current) { ... }
    public override int GenerateNext(IEnumerable&amp;lt;T&amp;gt; next) { ... }
    public override void Close() { ... }
    public override bool get_CheckClose() { ... }

    public override T get_LastGenerated()
    {
        return this.current;
    }
    public override IEnumerator&amp;lt;T&amp;gt; GetFreshEnumerator()
    {
        return new Chunk.ToEnumerable@18( ... );
    }
}&lt;/pre&gt;
&lt;p&gt;and in &lt;code&gt;GeneratedSequenceBase&lt;/code&gt; we have&lt;/p&gt;
&lt;pre class="brush: c#"&gt;private virtual void System-IDisposable-Dispose()
{
 if (this.redirect)
 {
  GeneratedSequenceBase&amp;lt;T&amp;gt; arg_11_0 = this.redirectTo;
  tail();
  arg_11_0.Close();
  return;
 }
 else
 {
  GeneratedSequenceBase&amp;lt;T&amp;gt; arg_1A_0 = this;
  tail();
  arg_1A_0.Close();
  return;
 }
}&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;Close&lt;/code&gt; method renders the enumeration non-functional, though the closed enumerator does not throw, it simply yields no more. That the resulting sequence has an active &lt;code&gt;Dispose&lt;/code&gt; behaviour, unlike the C# case, explains why in &lt;a href="http://stevegilham.blogspot.com/2011/04/generalized-ienumerable-to-chunked_12.html"&gt;the &lt;code&gt;Window&lt;/code&gt; example&lt;/a&gt; I needed to add the &lt;code&gt;Controlled&lt;/code&gt; wrapper to block that, so that the &lt;code&gt;Stream&lt;/code&gt; based enumeration written as a sequence expression would draw more than one tranche before stopping.&lt;/p&gt;
&lt;p&gt;By contrast, you don't need the &lt;code&gt;Controlled&lt;/code&gt; wrapper for windowing the BCL derived array-as-enumerator; presumably that has a no-op &lt;code&gt;Dispose&lt;/code&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-8698405704995073359?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/8698405704995073359/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=8698405704995073359' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/8698405704995073359'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/8698405704995073359'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/04/f-under-covers-xiii-sequence.html' title='F# under the covers XIII -- Sequence expressions'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-6114898297557055364</id><published>2011-04-12T23:07:00.002+01:00</published><updated>2011-05-02T00:01:00.141+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><title type='text'>Generalized IEnumerable to chunked IEnumerable in F#</title><content type='html'>&lt;p&gt;F# sequence expressions, under the covers, are complicated beasts; for the purposes of today's exercise, the factor of interest is that they &lt;code&gt;Dispose()&lt;/code&gt; internal enumerators aggressively, unlike in &lt;a href="http://stevegilham.blogspot.com/2011/04/generalized-ienumerable-to-chunked.html"&gt;the C# case&lt;/a&gt;, so the simple &lt;code&gt;Ratchet&lt;/code&gt; wrapper isn't enough by itself.  So, with some refactoring, we get&lt;/p&gt;
&lt;pre class="brush: f#"&gt;namespace Tinesware.Enumerables

open System
open System.Collections.Generic
open System.IO
open System.Linq
open Should.Fluent

module Chunk =

    /// &amp;lt;summary&amp;gt;
    /// Turns a stream into a byte enumeration
    /// &amp;lt;/summary&amp;gt;
    /// &amp;lt;param name="stream"&amp;gt;The stream to wrap&amp;lt;/param&amp;gt;
    /// &amp;lt;returns&amp;gt;The stream as an enumeration&amp;lt;/returns&amp;gt;
    let rec ToEnumerable (stream : Stream) =
        seq { 
            let result = stream.ReadByte()
            if result &amp;gt;= 0 then
              yield (byte result)
              yield! ToEnumerable stream
            stream.Close()
        }
        
    type private Controlled&amp;lt;'a&amp;gt;(iterator: IEnumerator&amp;lt;'a&amp;gt;) =
        interface IEnumerator&amp;lt;'a&amp;gt; with
          member self.Current = iterator.Current
        interface System.Collections.IEnumerator with
          member self.Current = iterator.Current :&amp;gt; Object
          member self.Reset() = iterator.Reset()
          member self.MoveNext() = iterator.MoveNext()
        interface IDisposable with
          member self.Dispose() = () // The whole purpose of the type -- prevent premature disposal
        
    type private Ratchet&amp;lt;'a&amp;gt;(iterator: IEnumerator&amp;lt;'a&amp;gt;) =
      interface IEnumerable&amp;lt;'a&amp;gt; with
        member self.GetEnumerator() = iterator
      interface System.Collections.IEnumerable with
        member self.GetEnumerator() = iterator :&amp;gt; System.Collections.IEnumerator
        
    /// &amp;lt;summary&amp;gt;
    /// Split an enumerable into an enumeration of arrays
    /// &amp;lt;/summary&amp;gt;
    /// &amp;lt;typeparam name="T"&amp;gt;Element type of the array&amp;lt;/typeparam&amp;gt;
    /// &amp;lt;param name="source"&amp;gt;The IEnumerable to operate on&amp;lt;/param&amp;gt;
    /// &amp;lt;param name="chunk"&amp;gt;The window size&amp;lt;/param&amp;gt;
    /// &amp;lt;returns&amp;gt;The enumeration&amp;lt;/returns&amp;gt;
    let Window (chunk : int) (source : 'a seq) =
        let rec innerWindowing x =
           seq {
               let result = x |&amp;gt; Seq.truncate chunk |&amp;gt; Seq.toArray
               if result.Length &amp;gt; 0 then
                 yield result
                 yield! innerWindowing x
               }
               
        innerWindowing &amp;lt;| new Ratchet&amp;lt;'a&amp;gt;(new Controlled&amp;lt;'a&amp;gt;(source.GetEnumerator()))

      
    /// &amp;lt;summary&amp;gt;
    /// A Self-test program
    /// &amp;lt;/summary&amp;gt;
    [&amp;lt;EntryPoint&amp;gt;]
    let main a =
        let input = seq { 0 .. 41 }
                    |&amp;gt; Seq.map byte
                    |&amp;gt; Seq.toArray
                    
        let inStream = new MemoryStream(input)
        let channel = inStream |&amp;gt; ToEnumerable

        let chunk = channel |&amp;gt; Seq.truncate 16 |&amp;gt; Seq.toArray
        chunk.Length.Should().Equal(16) |&amp;gt; ignore
        chunk.[0].Should().Equal((byte)0) |&amp;gt; ignore

        let chunk = channel |&amp;gt; Seq.truncate 16 |&amp;gt; Seq.toArray
        chunk.Length.Should().Equal(16) |&amp;gt; ignore
        chunk.[0].Should().Equal((byte)16) |&amp;gt; ignore

        let chunk = channel |&amp;gt; Seq.truncate 16 |&amp;gt; Seq.toArray
        chunk.Length.Should().Equal(10) |&amp;gt; ignore
        chunk.[0].Should().Equal((byte)32) |&amp;gt; ignore
        
        let chunks = input |&amp;gt; Window 16 |&amp;gt; Seq.toArray
        chunks.Length.Should().Equal(3) |&amp;gt; ignore
        chunks.[0].Length.Should().Equal(16) |&amp;gt; ignore
        chunks.[1].Length.Should().Equal(16) |&amp;gt; ignore
        chunks.[2].Length.Should().Equal(10) |&amp;gt; ignore
        
        let inStream = new MemoryStream(input)
        let chunks = inStream |&amp;gt; ToEnumerable |&amp;gt; Window 16 |&amp;gt; Seq.toArray
        chunks.Length.Should().Equal(3) |&amp;gt; ignore
        chunks.[0].Length.Should().Equal(16) |&amp;gt; ignore
        chunks.[1].Length.Should().Equal(16) |&amp;gt; ignore
        chunks.[2].Length.Should().Equal(10) |&amp;gt; ignore

        0&lt;/pre&gt;
&lt;p&gt;Note that this version closes the stream after the iteration completes -- we could leave the management of the stream entirely outside the iterator if we wished.  Since we are re-using our &lt;code&gt;Enumerator&lt;/code&gt; instance, we only have the one left for garbage collection, so suppressing the disposal doesn't litter too badly.&lt;/p&gt;
&lt;p&gt;Overall, if you're in the simple use-case of pulling chunks out of a file in order, the &lt;a href="http://stevegilham.blogspot.com/2011/04/stream-to-ienumerable-ienumerable-to.html"&gt;earlier version of the system&lt;/a&gt; will suffice.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;span style="font-weight:bold;"&gt;Edit 1-May-11&lt;/span&gt; : Comparing with &lt;a href="http://2sharp4u.wordpress.com/2011/05/01/efficient-sequence-operators-breakby/"&gt;2#4u's &lt;code&gt;Seq.breakBy&lt;/code&gt;&lt;/a&gt; I get on my laptop&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;Real: 00:00:03.948, CPU: 00:00:03.946 for &lt;code&gt;Seq.breakByV1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Real: 00:00:00.345, CPU: 00:00:00.374 for &lt;code&gt;Seq.breakByV2&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Real: 00:00:00.910, CPU: 00:00:00.904 for &lt;code&gt;Chunk.Window&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;making the functional &lt;code&gt;Chunk.Window&lt;/code&gt; implementation about 2.4 times slower than the faster -- but with more internal mutability -- of the the two.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-6114898297557055364?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/6114898297557055364/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=6114898297557055364' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/6114898297557055364'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/6114898297557055364'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/04/generalized-ienumerable-to-chunked_12.html' title='Generalized IEnumerable to chunked IEnumerable in F#'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-4733268298667107098</id><published>2011-04-12T20:07:00.006+01:00</published><updated>2011-04-12T20:49:46.682+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Generalized IEnumerable to chunked IEnumerable</title><content type='html'>&lt;p&gt;&lt;a href="http://stevegilham.blogspot.com/2011/04/stream-to-ienumerable-ienumerable-to.html?showComment=1302625989307#c6267543278113291998"&gt;As noted in the comments to the previous post&lt;/a&gt;, if you're not using the &lt;code&gt;Window&lt;/code&gt; on an Enumerable that maintains its own internal state (so that the different &lt;code&gt;IEnumerator&lt;/code&gt; yielded up each time will always be at the start of the iteration), it will loop forever.  So if you're not using the two in conjunction for the purpose of "read a file in chunks for e.g. passing over a network" or similar, you want this variant:&lt;/p&gt;
&lt;pre class="brush: c#"&gt;namespace Tinesware.Enumerables
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using Should.Fluent;

    /// &amp;lt;summary&amp;gt;
    /// More extension methods for enumerables
    /// &amp;lt;/summary&amp;gt;
    public static class Chunk
    {
        /// &amp;lt;summary&amp;gt;
        /// Turns a stream into an IEnumerable&amp;lt;byte&amp;gt;
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name="stream"&amp;gt;The stream to wrap&amp;lt;/param&amp;gt;
        /// &amp;lt;returns&amp;gt;The stream as an enumeration&amp;lt;/returns&amp;gt;
        public static IEnumerable&amp;lt;byte&amp;gt; ToEnumerable(this Stream stream)
        {
            while (true)
            {
                var result = stream.ReadByte();
                if (result &amp;gt;= 0)
                {
                    yield return (byte)result;
                }
                else
                {
                    break;
                }
            }
        }
        
        /// &amp;lt;summary&amp;gt;
        /// Split an IEnumerable&amp;lt;T&amp;gt; into an IEnumerable&amp;lt;T[]&amp;gt;
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;typeparam name="T"&amp;gt;Element type of the array&amp;lt;/typeparam&amp;gt;
        /// &amp;lt;param name="source"&amp;gt;The IEnumerable to operate on&amp;lt;/param&amp;gt;
        /// &amp;lt;param name="chunk"&amp;gt;The window size&amp;lt;/param&amp;gt;
        /// &amp;lt;returns&amp;gt;The enumeration&amp;lt;/returns&amp;gt;
        public static IEnumerable&amp;lt;T[]&amp;gt; Window&amp;lt;T&amp;gt;(this IEnumerable&amp;lt;T&amp;gt; source, int chunk)
        {
         var readonce = new Ratchet&amp;lt;T&amp;gt;(source);
         
            while (true)
            {
                var result = readonce.Take(chunk).ToArray();

                if (result.Any())
                {
                    yield return result;
                }
                else
                {
                    break;
                }
            }
        }

        private class Ratchet&amp;lt;T&amp;gt; : IEnumerable&amp;lt;T&amp;gt;
        {
            private readonly IEnumerator&amp;lt;T&amp;gt; iterator;
         
            public Ratchet(IEnumerable&amp;lt;T&amp;gt; source)
            {
                this.iterator = source.GetEnumerator();
            }
         
            public IEnumerator&amp;lt;T&amp;gt; GetEnumerator()
            {
                return iterator;
            }
         
            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
            {
                return iterator;
            }
        }

        /// &amp;lt;summary&amp;gt;
        /// A Self-test program
        /// &amp;lt;/summary&amp;gt;
        private static void Main()
        {
            var input = Enumerable.Range(0, 42).Select(x =&amp;gt; (byte)x).ToArray();
            var inStream = new MemoryStream(input);
            var channel = inStream.ToEnumerable();

            var chunk = channel.Take(16).ToArray();
            chunk.Length.Should().Equal(16);
            chunk[0].Should().Equal((byte)0);

            chunk = channel.Take(16).ToArray();
            chunk.Length.Should().Equal(16);
            chunk[0].Should().Equal((byte)16);

            chunk = channel.Take(16).ToArray();
            chunk.Length.Should().Equal(10);
            chunk[0].Should().Equal((byte)32);

            inStream.Position = 0;

            var chunks = inStream.ToEnumerable().Window(16).ToArray();
            chunks.Length.Should().Equal(3);
            chunks[0].Length.Should().Equal(16);
            chunks[1].Length.Should().Equal(16);
            chunks[2].Length.Should().Equal(10);
            
            chunks = input.Window(16).ToArray();
            chunks.Length.Should().Equal(3);
            chunks[0].Length.Should().Equal(16);
            chunks[1].Length.Should().Equal(16);
            chunks[2].Length.Should().Equal(10);
        }
    }
}&lt;/pre&gt;
&lt;p&gt;where the &lt;code&gt;Ratchet&lt;/code&gt; type ensures that the same &lt;code&gt;IEnumerator&lt;/code&gt; instance is yielded up each time -- at this point you have to be rather Jesuitical about what is immutable anyway, the sequence that returns you different mutable objects or the one that returns you the same object you may already have mutated...&lt;/p&gt;
&lt;p&gt;F# is less convenient for this one, though :(&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-4733268298667107098?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/4733268298667107098/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=4733268298667107098' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/4733268298667107098'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/4733268298667107098'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/04/generalized-ienumerable-to-chunked.html' title='Generalized IEnumerable to chunked IEnumerable'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-8289411351423724879</id><published>2011-04-09T09:41:00.006+01:00</published><updated>2011-04-09T17:06:36.503+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>App.config or app.exe.config? Open Question.</title><content type='html'>&lt;p&gt;Another thing that caught me on W2k3, 64-bit, recently, is that on some, but not all, systems if I have an application &lt;code&gt;appname.exe&lt;/code&gt; which is C#, .net 3.5 sp1, compiled in AnyCPU mode, but installed to &lt;code&gt;Program Files (x86)&lt;/code&gt;, if you get the configuration file path by&lt;/p&gt;
&lt;pre class="brush: c#"&gt;var path = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).FilePath&lt;/pre&gt;
&lt;p&gt;it may report &lt;code&gt;appname.config&lt;/code&gt;, as opposed to the more usual &lt;code&gt;appname.exe.config&lt;/code&gt; as the file name.&lt;/p&gt;
&lt;p&gt;And when that happens, it is not joking -- if your installer puts a file at &lt;code&gt;appname.exe.config&lt;/code&gt; on such a system, &lt;span style="font-style:italic;"&gt;your program will not read it&lt;/span&gt;.  If you manually move the file to &lt;code&gt;appname.config&lt;/code&gt; and restart the application, all will be well.&lt;/p&gt;
&lt;p&gt;The internet hasn't been very forthcoming in either cause or remedy -- I did find several copies of a &lt;a href="http://www.vistaheads.com/forums/microsoft-public-windows-vista-general/98035-app-exe-config-xp-but-app-config-vista-x64.html"&gt;similar issue&lt;/a&gt; for 32-bit apps on Vista x64, but no resolution.&lt;/p&gt;
&lt;p&gt;There's a similar &lt;a href="https://connect.microsoft.com/VisualStudio/feedback/details/375671/default-app-config-file-name-breaking-change-in-net-3-5"&gt;bug report against .net 3.5&lt;/a&gt; -- but by observation the issue only happens in a minority of cases.  The only difference I've spotted between two machines where one shows the bug and the other doesn't is that the bug shows in the one with .net 3.0 and 3.5 sp1 not fully patched up to date -- but the patches don't touch any of the libraries that are likely to be involved (System.Configuration and dependencies).&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-8289411351423724879?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/8289411351423724879/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=8289411351423724879' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/8289411351423724879'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/8289411351423724879'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/04/appconfig-or-appexeconfig.html' title='App.config or app.exe.config? Open Question.'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-6157689077590546826</id><published>2011-04-09T09:30:00.004+01:00</published><updated>2011-04-10T14:25:13.109+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MSFT utilities'/><title type='text'>More than you wanted to know about sc.exe and service isolation in Windows 2k3 SP2</title><content type='html'>&lt;p&gt;At the level of Win2k3 SP2 unpatched, sc.exe did not support the &lt;code&gt;sidtype&lt;/code&gt; parameter for setting a per-service SID.  If you issue&lt;/p&gt;
&lt;pre class="code"&gt;sc.exe sidtype MyService Unrestricted&lt;/pre&gt;
&lt;p&gt;it spits out help text, then pauses for user input&lt;/p&gt;
&lt;pre class="code"&gt;Would you like to see help for the QUERY and QUERYEX commands? [ y | n ]:&lt;/pre&gt;
&lt;p&gt;At patch MS09-012/KB959454 (or maybe KB956572), if not before, sc.exe was upgraded to interpret this parameter.  However...&lt;/p&gt;
&lt;p&gt;If on 64-bit Win 2k3 with that patch, you run the 32-bit -- &lt;code&gt;Program Files (x86)&lt;/code&gt; -- version, that will accept the &lt;code&gt;sidtype&lt;/code&gt; parameter &lt;span style="font-style:italic;"&gt;and do nothing&lt;/span&gt;, at least on the various systems I've tested on.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-6157689077590546826?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/6157689077590546826/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=6157689077590546826' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/6157689077590546826'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/6157689077590546826'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/04/more-than-you-wanted-to-know-about.html' title='More than you wanted to know about sc.exe and service isolation in Windows 2k3 SP2'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-6956161534530718685</id><published>2011-04-08T23:30:00.002+01:00</published><updated>2011-04-08T23:38:43.342+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Stream to IEnumerable; IEnumerable to chunked IEnumerable</title><content type='html'>&lt;p&gt;I may have missed these in F#, and they don't seem at all obvious in C# standard libraries.  A couple of handy functions/extension methods with simple tests using &lt;a href="http://should.codeplex.com/"&gt;the Should.Fluent assertion library&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In F#:&lt;/p&gt;
&lt;pre class="brush: f#"&gt;namespace Tinesware.Enumerables

open System
open System.Collections.Generic
open System.IO
open System.Linq
open Should.Fluent

module Chunk =

    /// &amp;lt;summary&amp;gt;
    /// Turns a stream into a byte sequence
    /// &amp;lt;/summary&amp;gt;
    /// &amp;lt;param name="stream"&amp;gt;The stream to wrap&amp;lt;/param&amp;gt;
    /// &amp;lt;returns&amp;gt;The stream as an enumeration&amp;lt;/returns&amp;gt;
    let ToEnumerable (stream : Stream) =
        seq { 
            let result = ref &amp;lt;| stream.ReadByte()
            while !result &amp;gt;= 0 do
              yield (byte !result)
              result := stream.ReadByte()
        }
        
    /// &amp;lt;summary&amp;gt;
    /// Split a 'a seq into an 'a[] seq
    /// &amp;lt;/summary&amp;gt;
    /// &amp;lt;typeparam name="'a"&amp;gt;Element type of the array&amp;lt;/typeparam&amp;gt;
    /// &amp;lt;param name="source"&amp;gt;The IEnumerable to operate on&amp;lt;/param&amp;gt;
    /// &amp;lt;param name="chunk"&amp;gt;The window size&amp;lt;/param&amp;gt;
    /// &amp;lt;returns&amp;gt;The enumeration&amp;lt;/returns&amp;gt;
    let Window (chunk : int) (source : 'a seq) =
        seq { 
            let result = ref (source |&amp;gt; Seq.truncate chunk |&amp;gt; Seq.toArray)
            while (!result).Length &amp;gt; 0 do
              yield !result
              result := source |&amp;gt; Seq.truncate chunk |&amp;gt; Seq.toArray
        }
      
    /// &amp;lt;summary&amp;gt;
    /// A Self-test program
    /// &amp;lt;/summary&amp;gt;
    [&amp;lt;EntryPoint&amp;gt;]
    let main a =
        let input = seq { 0 .. 41 }
                    |&amp;gt; Seq.map byte
                    |&amp;gt; Seq.toArray
                    
        let inStream = new MemoryStream(input)
        let channel = inStream |&amp;gt; ToEnumerable

        let chunk = channel |&amp;gt; Seq.truncate 16 |&amp;gt; Seq.toArray
        chunk.Length.Should().Equal(16) |&amp;gt; ignore
        chunk.[0].Should().Equal((byte)0) |&amp;gt; ignore

        let chunk = channel |&amp;gt; Seq.truncate 16 |&amp;gt; Seq.toArray
        chunk.Length.Should().Equal(16) |&amp;gt; ignore
        chunk.[0].Should().Equal((byte)16) |&amp;gt; ignore

        let chunk = channel |&amp;gt; Seq.truncate 16 |&amp;gt; Seq.toArray
        chunk.Length.Should().Equal(10) |&amp;gt; ignore
        chunk.[0].Should().Equal((byte)32) |&amp;gt; ignore

        inStream.Position &amp;lt;- int64 0

        let chunks = inStream |&amp;gt; ToEnumerable |&amp;gt; Window 16 |&amp;gt; Seq.toArray
        chunks.Length.Should().Equal(3) |&amp;gt; ignore
        chunks.[0].Length.Should().Equal(16) |&amp;gt; ignore
        chunks.[1].Length.Should().Equal(16) |&amp;gt; ignore
        chunks.[2].Length.Should().Equal(10) |&amp;gt; ignore

        0&lt;/pre&gt;
&lt;p&gt;In C#:&lt;/p&gt;
&lt;pre class="brush: c#"&gt;namespace Tinesware.Enumerables
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using Should.Fluent;

    /// &amp;lt;summary&amp;gt;
    /// More extension methods for enumerables
    /// &amp;lt;/summary&amp;gt;
    public static class Chunk
    {
        /// &amp;lt;summary&amp;gt;
        /// Split an IEnumerable&amp;lt;T&amp;gt; into an IEnumerable&amp;lt;T[]&amp;gt;
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;typeparam name="T"&amp;gt;Element type of the array&amp;lt;/typeparam&amp;gt;
        /// &amp;lt;param name="source"&amp;gt;The IEnumerable to operate on&amp;lt;/param&amp;gt;
        /// &amp;lt;param name="chunk"&amp;gt;The window size&amp;lt;/param&amp;gt;
        /// &amp;lt;returns&amp;gt;The enumeration&amp;lt;/returns&amp;gt;
        public static IEnumerable&amp;lt;T[]&amp;gt; Window&amp;lt;T&amp;gt;(this IEnumerable&amp;lt;T&amp;gt; source, int chunk)
        {
            while (true)
            {
                var result = source.Take(chunk).ToArray();

                if (result.Any())
                {
                    yield return result;
                }
                else
                {
                    break;
                }
            }
        }

        /// &amp;lt;summary&amp;gt;
        /// Turns a stream into an IEnumerable&amp;lt;byte&amp;gt;
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name="stream"&amp;gt;The stream to wrap&amp;lt;/param&amp;gt;
        /// &amp;lt;returns&amp;gt;The stream as an enumeration&amp;lt;/returns&amp;gt;
        public static IEnumerable&amp;lt;byte&amp;gt; ToEnumerable(this Stream stream)
        {
            while (true)
            {
                var result = stream.ReadByte();
                if (result &amp;gt;= 0)
                {
                    yield return (byte)result;
                }
                else
                {
                    break;
                }
            }
        }

        /// &amp;lt;summary&amp;gt;
        /// A Self-test program
        /// &amp;lt;/summary&amp;gt;
        private static void Main()
        {
            var input = Enumerable.Range(0, 42).Select(x =&amp;gt; (byte)x).ToArray();
            var inStream = new MemoryStream(input);
            var channel = inStream.ToEnumerable();

            var chunk = channel.Take(16).ToArray();
            chunk.Length.Should().Equal(16);
            chunk[0].Should().Equal((byte)0);

            chunk = channel.Take(16).ToArray();
            chunk.Length.Should().Equal(16);
            chunk[0].Should().Equal((byte)16);

            chunk = channel.Take(16).ToArray();
            chunk.Length.Should().Equal(10);
            chunk[0].Should().Equal((byte)32);

            inStream.Position = 0;

            var chunks = inStream.ToEnumerable().Window(16).ToArray();
            chunks.Length.Should().Equal(3);
            chunks[0].Length.Should().Equal(16);
            chunks[1].Length.Should().Equal(16);
            chunks[2].Length.Should().Equal(10);
        }
    }
}&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-6956161534530718685?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/6956161534530718685/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=6956161534530718685' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/6956161534530718685'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/6956161534530718685'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/04/stream-to-ienumerable-ienumerable-to.html' title='Stream to IEnumerable; IEnumerable to chunked IEnumerable'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-7767815338602419705</id><published>2011-04-06T22:52:00.002+01:00</published><updated>2011-04-06T23:37:34.088+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='book'/><title type='text'>Q1 in books</title><content type='html'>&lt;p&gt;More fiction reading than I usually do these days, mainly from the grotty weather and having spotted a bunch of titles of interest to add to the Christmas list:&lt;/p&gt;
&lt;h4&gt;&lt;span style="font-style:italic;"&gt;The Quantum Thief&lt;/span&gt; by Hannu Rajaniemi&lt;/h4&gt;
&lt;p&gt;Somewhat reminiscent of early Roger Zelazny -- definitely not the Egan-esque fare suggested by other reviews -- as if Zelazny had been handed Schroeder's &lt;span style="font-style:italic;"&gt;Lady of Mazes&lt;/span&gt; and a genre trope update for the next 40-odd years when he was starting to write &lt;span style="font-style:italic;"&gt;Today We Choose Faces&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Good, not great.&lt;/p&gt;
&lt;h4&gt;&lt;span style="font-style:italic;"&gt;Kraken&lt;/span&gt; by China Mi&amp;eacute;ville&lt;/h4&gt;
&lt;p&gt;After &lt;span style="font-style:italic;"&gt;The City and the City&lt;/span&gt;, doing yet another magical London seems rather a return to the formulaic. At least he resisted -- given the title and the main character's name -- doing any "Billy the Squid" jokes.&lt;/p&gt;
&lt;p&gt;Only pedestrian by comparison with his other works.&lt;/p&gt;
&lt;h4&gt;&lt;span style="font-style:italic;"&gt;The Quiet War/Gardens of the Sun&lt;/span&gt; by Paul McAuley&lt;/h4&gt;
&lt;p&gt;You just have to take the next couple of hundred years future history in this set-up as a given, to provide the backdrop for a high-tech, solar-system spanning tale of Byzantine politics, interspersed with travelogue, the latter possibly becoming a bit repetitive each time we are shown a new piece of airless real-estate.&lt;/p&gt;
&lt;p&gt;The ending is fortunately not telegraphed -- whether the not-so-bad guys or the greasy-pole climbers will win out is finely balanced almost all the way.&lt;/p&gt;
&lt;h4&gt;&lt;span style="font-style:italic;"&gt;Wayland's Principia&lt;/span&gt; by Richard Garfinkle&lt;/h4&gt;
&lt;p&gt;&lt;a href="http://www.achronalpress.com/wasample.html"&gt;This time, he takes on the old staple of First Contact&lt;/a&gt;, weaving the alien into territory more usually seen in myth -- it's all about its construction of aliens and where the not-human becomes a monster, and how the gulf might be bridged.&lt;/p&gt;
&lt;p&gt;As such it's not set up as something that can have a tidy end, but rather fades out into the future, the narrative sort of pulling back and speeding up in the last few pages, until it rather petered out in the end.  A good and interesting book, but not, I feel, his best work.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-7767815338602419705?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/7767815338602419705/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=7767815338602419705' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/7767815338602419705'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/7767815338602419705'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/04/q1-in-books.html' title='Q1 in books'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-2510235489137323036</id><published>2011-04-06T22:30:00.002+01:00</published><updated>2011-04-06T22:50:44.170+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='weather'/><category scheme='http://www.blogger.com/atom/ns#' term='garden'/><title type='text'>Full Spring</title><content type='html'>&lt;p&gt;After several weeks where the weather has been generally miserable -- at best leaden skies and wind, though not so bad as to stop me putting in a a few days of cycling to work -- and unexpected fire-fighting at work has been sapping my energies, today was, after a dull start, full of promise of summer.  Let's hope it doesn't turn into one of those years where Q2 is put on back-to-front.&lt;/p&gt;
&lt;p&gt;In the garden, the forsythia is finishing, the earliest tulips have finished, but the daffs, grape hyacinths and main tulips are in full flower; while the primroses carry on.  And a fair number of new plants from last year failed to overwinter -- one of the lavenders, most of the geraniums, despite the latter being in a notionally frost-free greenhouse.&lt;/p&gt;
&lt;p&gt;Still, the weather was mild enough weekend before last to take the surviving plants out, and fumigate the greenhouse; and I could then use the space to dry out the lawn weed-and-feed that had gotten damp in the shed over the winter.  So the lawn dressing belatedly happened last weekend, a couple of weeks later than usual.  And now the first of the orders of plants arrived, so there are tomatoes in pots, and chilis and overwintered perpetual spinach in the greenhouse; with salad leaves and basil planted as seeds.&lt;/p&gt;
&lt;p&gt;Out in the rest of the garden, the bed for annuals has been dug, raked &amp; weeded, so is ready for scattering seed, sunflowers are in a seed-tray, waiting to germinate, and the green bin full to the brim again, with over a week to the next collection -- showing how suddenly I have to make up for lost time (some years gardening started in mid-February, so I'm at least a month behind, plus having still to catch up from the abruptly truncated autumn).&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-2510235489137323036?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/2510235489137323036/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=2510235489137323036' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/2510235489137323036'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/2510235489137323036'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/04/full-spring.html' title='Full Spring'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-2013427078054203176</id><published>2011-04-06T22:23:00.002+01:00</published><updated>2011-04-06T22:28:57.284+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='IronPython'/><title type='text'>Using LINQ/Extension methods with IronPython 2.7's clr.ImportExtensions method</title><content type='html'>&lt;p&gt;Woefully underdocumented on the internet, I worked this one out by analogy to the &lt;a href="http://ironruby.codeplex.com/releases/view/43540#ReleaseDescriptionPanel"&gt;IronRuby &lt;code&gt;using_clr_extensions&lt;/code&gt; method&lt;/a&gt;:&lt;/p&gt;
&lt;pre class="brush: py"&gt;&gt;&amp; 'C:\Program Files\IronPython 2.7\ipy.exe'
IronPython 2.7 (2.7.0.40) on .NET 4.0.30319.225
Type "help", "copyright", "credits" or "license" for more information.
&gt;&gt;&gt; import clr
&gt;&gt;&gt; clr.AddReference("System.Core")
&gt;&gt;&gt; from System.Collections.Generic import List
&gt;&gt;&gt; dir(List)
['Add', 'AddRange', 'AsReadOnly', 'BinarySearch', 'Capacity', 'Clear', 'Contains', 'ConvertAll', 'CopyTo', 'Count', 'Enu
merator', 'Equals', 'Exists', 'Find', 'FindAll', 'FindIndex', 'FindLast', 'FindLastIndex', 'ForEach', 'GetEnumerator', '
GetHashCode', 'GetRange', 'GetType', 'IndexOf', 'Insert', 'InsertRange', 'IsReadOnly', 'IsSynchronized', 'Item', 'LastIn
dexOf', 'MemberwiseClone', 'ReferenceEquals', 'Remove', 'RemoveAll', 'RemoveAt', 'RemoveRange', 'Reverse', 'Sort', 'Sync
Root', 'ToArray', 'ToString', 'TrimExcess', 'TrueForAll', '__add__', '__class__', '__contains__', '__delattr__', '__doc_
_', '__format__', '__getattribute__', '__getitem__', '__hash__', '__init__', '__iter__', '__len__', '__new__', '__reduce
__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__']
&gt;&gt;&gt; import System
&gt;&gt;&gt; clr.ImportExtensions(System.Linq)
&gt;&gt;&gt; dir (List)
['Add', 'AddRange', 'Aggregate', 'All', 'Any', 'AsEnumerable', 'AsParallel', 'AsQueryable', 'AsReadOnly', 'Average', 'Bi
narySearch', 'Capacity', 'Cast', 'Clear', 'Concat', 'Contains', 'ConvertAll', 'CopyTo', 'Count', 'DefaultIfEmpty', 'Dist
inct', 'ElementAt', 'ElementAtOrDefault', 'Enumerator', 'Equals', 'Except', 'Exists', 'Find', 'FindAll', 'FindIndex', 'F
indLast', 'FindLastIndex', 'First', 'FirstOrDefault', 'ForEach', 'GetEnumerator', 'GetHashCode', 'GetRange', 'GetType',
'GroupBy', 'GroupJoin', 'IndexOf', 'Insert', 'InsertRange', 'Intersect', 'IsReadOnly', 'IsSynchronized', 'Item', 'Join',
 'Last', 'LastIndexOf', 'LastOrDefault', 'LongCount', 'Max', 'MemberwiseClone', 'Min', 'OfType', 'OrderBy', 'OrderByDesc
ending', 'ReferenceEquals', 'Remove', 'RemoveAll', 'RemoveAt', 'RemoveRange', 'Reverse', 'Select', 'SelectMany', 'Sequen
ceEqual', 'Single', 'SingleOrDefault', 'Skip', 'SkipWhile', 'Sort', 'Sum', 'SyncRoot', 'Take', 'TakeWhile', 'ToArray', '
ToDictionary', 'ToList', 'ToLookup', 'ToString', 'TrimExcess', 'TrueForAll', 'Union', 'Where', 'Zip', '__add__', '__clas
s__', '__contains__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__getitem__', '__hash__', '__init__',
 '__iter__', '__len__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__'
, '__str__', '__subclasshook__']&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-2013427078054203176?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/2013427078054203176/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=2013427078054203176' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/2013427078054203176'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/2013427078054203176'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/04/using-linqextension-methods-with.html' title='Using LINQ/Extension methods with IronPython 2.7&apos;s clr.ImportExtensions method'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-8637538024367403837</id><published>2011-03-01T20:55:00.003Z</published><updated>2011-03-01T21:07:51.205Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Uninstalling the uninstallable -- old, dead Java Consoles in Firefox</title><content type='html'>&lt;p&gt;There they are, every time.  You say no to the Yahoo! toolbar, but each time you update your Java runtine, your extensions list accumulates another disabled Java Console 6.0.xx that won't uninstall through Firefox.&lt;/p&gt;
&lt;p&gt;To get rid of an old 6.0.xx extension, go to &lt;code&gt;C:\Program Files\Mozilla Firefox\extensions&lt;/code&gt; and just delete the folder called &lt;code&gt;{CAFEEFAC-0016-0000-00xx-ABCDEFFEDCBA}&lt;/code&gt;, and you're sorted.&lt;/p&gt;
&lt;p&gt;You can use the same trick to get rid of other extensions that won't take a hint (e.g. the Skype extension).  The folder is another &lt;code&gt;{Guid}&lt;/code&gt; -- look in the chrome.manifest file inside the folder for one containing appropriate names; or check the date of the folder against the date you last installed/updated.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-8637538024367403837?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/8637538024367403837/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=8637538024367403837' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/8637538024367403837'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/8637538024367403837'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/03/unin-stalling-uninstallable-old-dead.html' title='Uninstalling the uninstallable -- old, dead Java Consoles in Firefox'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-6419358634488598594</id><published>2011-02-27T11:03:00.002Z</published><updated>2011-02-27T11:11:26.604Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='book'/><category scheme='http://www.blogger.com/atom/ns#' term='anime'/><title type='text'>Book — Summer of the Ubume by Natsuhiko Kyogoku</title><content type='html'>&lt;p&gt;The first novel by Natsuhiko Kyogoku, and the one to which &lt;span style="font-style:italic;"&gt;&lt;a href="http://stevegilham.blogspot.com/2009/05/lost-in-translation-mouryou-no-hako.html"&gt;Mouryou no Hako&lt;/a&gt;&lt;/span&gt; is a sequel.&lt;/p&gt;
&lt;p&gt;The prose is rendered nicely into English, while retaining some of the stylistics tics of the original (no sentence broken between pages, at least for the first 200-odd pages); there are a few translators notes where allusions are made to various folk-tales. The most obtrusive translation quirks are where dates are concerned&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;The book he was reading was something from the Edo Period [1603-1868]...&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;but those are quickly past after the opening chapter.&lt;/p&gt;
&lt;p&gt;Many of the cast of &lt;span style="font-style:italic;"&gt;Mouryou no Hako&lt;/span&gt; appear in this first novel -- many more than I had expected. And Kyogoku seems to have a thing about bizarre hospitals.&lt;/p&gt;
&lt;p&gt;By the end, this is clearly a strong first novel -- and it's also clear why the next book he wrote about the same cast was the one that got the adaptations : this one relied just a bit too much on the power of coincidence (three different characters suffering the same hysterical blindness, for one), and on having some of the main cast involved at some point in the past with one or other of the major players in the mystery.&lt;/p&gt;
&lt;p&gt;Still, if you enjoyed the &lt;span style="font-style:italic;"&gt;Mouryou no Hako&lt;/span&gt; anime, this is the nearest thing you'll get to having another fix of the same in English language.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-6419358634488598594?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/6419358634488598594/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=6419358634488598594' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/6419358634488598594'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/6419358634488598594'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/02/book-summer-of-ubume-by-natsuhiko.html' title='Book &amp;#8212; &lt;i&gt;Summer of the Ubume&lt;/i&gt; by Natsuhiko Kyogoku'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-2337150048842557401</id><published>2011-02-27T10:57:00.002Z</published><updated>2011-02-27T11:01:47.322Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='weather'/><category scheme='http://www.blogger.com/atom/ns#' term='cycling'/><category scheme='http://www.blogger.com/atom/ns#' term='garden'/><title type='text'>Signs of Spring</title><content type='html'>&lt;p&gt;Sunlight through the curtains and birdsong woke me at just after 07:00 today.  In the garden, the snowdrops are fading after having gone strong all month, and the forsythia has been out for over a week.  Purple crocuses are joining the yellow ones; and the primroses in shadier parts are flowering, following the ones in the sunny bed by the greenhouse wall, which have been out for weeks.&lt;/p&gt;
&lt;p&gt;Monday, as most of the office were out karting, I took the chance to cycle to work, and bunk off ahead of dusk and rain; but it'll be a couple of weeks yet before starting to do it around the normal working day.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-2337150048842557401?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/2337150048842557401/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=2337150048842557401' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/2337150048842557401'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/2337150048842557401'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/02/signs-of-spring_27.html' title='Signs of Spring'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-4490169121788164353</id><published>2011-02-19T22:24:00.006Z</published><updated>2011-04-06T22:58:53.746+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='mono'/><title type='text'>Mono 2.10, XBuild, F# -- first attempts</title><content type='html'>&lt;p&gt;Following the &lt;a href="http://www.mono-project.com/Release_Notes_Mono_2.10#Languages"&gt;announcement that F# and the Iron languages&lt;/a&gt; are being bundled with Mono 2.10 for Mac and Linux, I thought it worth taking the system for a spin on Windows as well.  This is a record of my experiments, and are nowhere near a complete HOW-TO as yet (they more closely resemble a HOW NOT TO at the moment).&lt;/p&gt;
&lt;p&gt;The easy bit is as per &lt;a href="http://xtzgzorex.wordpress.com/2011/01/08/f-and-xbuild-debian/"&gt;Zor's blog, here&lt;/a&gt; -- &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;copy &lt;code&gt;C:\program files\MSBuild\FSharp&lt;/code&gt; to &lt;code&gt;C:\Program Files\Mono-2.10\lib\mono\xbuild&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Get the mono strong-name key &lt;code&gt;https://github.com/mono/mono/blob/master/mcs/class/mono.snk&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Now this is voodoo -- without going &lt;code&gt;&amp;amp; 'C:\Program Files\Mono-2.10\bin\sn.bat' -R .\FSharp.Core.dll .\mono.snk&lt;/code&gt; on a copy of the F# runtime, the GACing step fails : yet the operation doesn't actually change the embedded key (going as far as &lt;code&gt;ILDasm&lt;/code&gt;/&lt;code&gt;ILAsm&lt;/code&gt; seems a bit extreme!)&lt;/li&gt;
&lt;li&gt;As administrator, &lt;code&gt;&amp;amp; 'C:\Program Files\Mono-2.10\bin\mono.exe' 'C:\Program Files\Mono-2.10\lib\mono\2.0\gacutil.exe' -i '.\FSharp.Core.dll'&lt;/code&gt;, and then copy the original &lt;code&gt;Fsharp.core.sigdata&lt;/code&gt; and &lt;code&gt;.optdata&lt;/code&gt; alongside&lt;/li&gt;
&lt;li&gt;in &lt;code&gt;C:\Program Files\Mono-2.10\lib\mono\3.5\Microsoft.Common.targets&lt;/code&gt; undo the commenting out of &lt;code&gt;_ComputeNonExistentFileProperty&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;In the copy of the &lt;code&gt;Microsoft.Sharp.targets&lt;/code&gt; file, comment out the &lt;code&gt;&amp;lt;ItemGroup Remove="..."&amp;gt;&lt;/code&gt; clauses after &lt;code&gt;Fsc&lt;/code&gt; task in the &lt;code&gt;CoreCompile&lt;/code&gt; target, and the two in &lt;code&gt;CreateManifestResourceNames&lt;/code&gt; (might there be a &lt;code&gt;CreateItem&lt;/code&gt; equivalent?  Haven't tried yet).&lt;/li&gt;&lt;/ul&gt;

&lt;p&gt;At this point, pure F# projects for VS2008 will build happily, so long as you don't have any fancy modern task attributes in &lt;code&gt;BeforeBuild&lt;/code&gt; or &lt;code&gt;AfterBuild&lt;/code&gt; (e.g. &lt;code&gt;Error executing task Copy: Task does not have property "OverwriteReadOnlyFiles" defined&lt;/code&gt; if you try to use that).&lt;/p&gt;

&lt;p&gt;Where I have yet to manage to persuade the build process to work is for projects with embedded resources (such as Glade XML definitions for a UI).  These yield up&lt;/p&gt;

&lt;pre class="code"&gt;        Target CopyNonResxEmbeddedResources:
C:\PROGRA~1\Mono-2.10\lib\mono\3.5\Microsoft.Common.targets: error : 
You must specify DestinationFolder or DestinationFiles attribute.
        Task "Copy" execution -- FAILED&lt;/pre&gt;

&lt;p&gt;which may possibly relate to some of the &lt;code&gt;&amp;lt;ItemGroup Remove="..."&amp;gt;&lt;/code&gt; operations not being carried out.  Certainly, some more MSBuild hackery will be required to track down what is going on and make sure that both sides of the copy operation match up -- so that either the source is empty, and no copy is attempted, or so that the destination doesn't end up null.&lt;/p&gt;

&lt;p&gt;Support for VS2010 projects isn't there yet -- after &lt;code&gt;sn -R&lt;/code&gt; (with equally little effect on the strong-name given by &lt;code&gt;sn -Tp&lt;/code&gt;) and GACing the 4.0 version of FSharp.Core.dll, trying to build a VS2010 project yields up the cryptic message, sometimes as an error, sometimes as a warning, that &lt;code&gt;"Operation is not valid due to the current state of the object"&lt;/code&gt; -- even with &lt;code&gt;/verbosity:diagnostic&lt;/code&gt; turned on no more detail is divulged.  And having the 4.0 version GAC'd interferes with the 2.0 version in the GAC -- the later version is selected by preference and then fails for being incompatible with the 2.0-level mscorlib when building a vanilla VS2008 project with just a simple reference (without explicit version information like &lt;code&gt;&amp;lt;Reference Include="FSharp.Core" /&amp;gt;&lt;/code&gt;) to the F# runtime.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-4490169121788164353?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/4490169121788164353/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=4490169121788164353' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/4490169121788164353'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/4490169121788164353'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/02/mono-210-xbuild-f-first-attempts.html' title='Mono 2.10, XBuild, F# -- first attempts'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-7985170930916150187</id><published>2011-02-18T07:22:00.005Z</published><updated>2011-02-18T07:56:25.983Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><title type='text'>ILSpy and F#</title><content type='html'>&lt;p&gt;Suddenly a new contender in the post-Reflector age -- &lt;a href="http://wiki.sharpdevelop.net/ilspy.ashx"&gt;ILSpy&lt;/a&gt;, emerging from the SharpDevelop stable.&lt;/p&gt;
&lt;p&gt;So I decided to give it a whirl with the same F# code as with &lt;a href="http://stevegilham.blogspot.com/2011/02/cecildecompiler-and-f.html"&gt;the simple tests I made last week with Cecil.Decompiler&lt;/a&gt; -- a few extension methods for &lt;code&gt;Option&lt;/code&gt;&lt;/p&gt;
&lt;pre class="brush: f#"&gt;module Augment =   

  type Microsoft.FSharp.Core.Option&amp;lt;'T&amp;gt; with
      static member filter (f : 'T -&amp;gt; bool) (x : option&amp;lt;'T&amp;gt;) =
        match x with
        | Some v when f(v) -&amp;gt; Some v
        | _ -&amp;gt; None
        
      static member getOrElse (fallback : 'T) (x : option&amp;lt;'T&amp;gt;) = defaultArg x fallback

      static member select (f : 'T -&amp;gt; bool) (x : 'T) =
        if f(x) then Some x
        else None
        
      static member nullable (x : 'a when 'a : null) : option&amp;lt;'a&amp;gt; =
        if x &amp;lt;&amp;gt; null then Some x
        else None&lt;/pre&gt;

&lt;p&gt;The first thing I notice is that I can't copy and paste code from the decompilation screen -- I have to save it, and which point I get a button to open Explorer in the same directory as I saved.  The C# code generated looks sane&lt;/p&gt;
&lt;pre class="brush: c#"&gt;public static FSharpOption&amp;lt;T&amp;gt; Option`1.select.Static(FSharpFunc&amp;lt;T, bool&amp;gt; f, T x)
{
 if (f.Invoke(x))
 {
 }
 else
 {
  goto Block_3;
 }
 return FSharpOption&amp;lt;T&amp;gt;.Some(x);
 Block_3:
 return null;
}&lt;/pre&gt;
&lt;p&gt;Unlike Cecil, this gets the F# branching right in representing the highlighted section&lt;/p&gt;

&lt;pre class="brush: c#; highlight: [5, 6, 7]"&gt;        IL_0000: nop
        IL_0001: ldarg.0
        IL_0002: ldarg.1
        IL_0003: callvirt !1 Microsoft.FSharp.Core.FSharpFunc`2&amp;lt;T,System.Boolean&amp;gt;::Invoke(!0)
        IL_0008: brfalse.s IL_000c
        IL_000a: br.s IL_000e
        IL_000c: br.s IL_0015
        IL_000e: ldarg.1
        IL_000f: call Microsoft.FSharp.Core.FSharpOption`1&amp;lt;!0&amp;gt; Microsoft.FSharp.Core.FSharpOption`1&amp;lt;T&amp;gt;::Some(!0)
        IL_0014: ret
        IL_0015: ldnull
        IL_0016: ret&lt;/pre&gt;
&lt;p&gt;It also handles compiler generated temporaries just fine -- albeit with the same 'C'-style if&lt;/p&gt;
&lt;pre class="brush: c#; highlight: [4]"&gt;public static FSharpOption&amp;lt;T&amp;gt; Option`1.filter.Static(FSharpFunc&amp;lt;T, bool&amp;gt; f, FSharpOption&amp;lt;T&amp;gt; x)
{
 FSharpOption&amp;lt;T&amp;gt; fSharpOption = x;
 if (fSharpOption)
 {
  FSharpOption&amp;lt;T&amp;gt; fSharpOption2 = fSharpOption;
  T t = fSharpOption2.Value;
  if (f.Invoke(t))
  {
   goto Block_4;
  }
 }
 return null;
 Block_4:
 return FSharpOption&amp;lt;T&amp;gt;.Some(fSharpOption2.Value);
}&lt;/pre&gt;
&lt;p&gt;And -- bonus -- while Cecil choked on this C# code&lt;/p&gt;
&lt;pre class="brush: c#"&gt;private static void DumpAssembly(string path)
  {
   var assembly = AssemblyDefinition.ReadAssembly(path);
   var pdbpath = Path.ChangeExtension (path, ".pdb");
   var provider = new PdbReaderProvider();
            var reader = provider.GetSymbolReader(assembly.MainModule, pdbpath);
            assembly.MainModule.ReadSymbols(reader);

   assembly.MainModule.Types.SelectMany(x =&amp;gt; x.Methods).ToList().ForEach(Decompile);    
  }&lt;/pre&gt;
&lt;p&gt;ILSpy comes up with&lt;/p&gt;
&lt;pre class="brush: c#"&gt;private static void DumpAssembly(string path)
{
 AssemblyDefinition assemblyDefinition = AssemblyDefinition.ReadAssembly(path);
 string text = Path.ChangeExtension(path, ".pdb");
 ISymbolReader symbolReader = new PdbReaderProvider().GetSymbolReader(assemblyDefinition.MainModule, text);
 assemblyDefinition.MainModule.ReadSymbols(symbolReader);
 var arg_5F_0 = assemblyDefinition.MainModule.Types;
 if (!Program.CS$&amp;lt;&amp;gt;9__CachedAnonymousMethodDelegate1)
 {
  Program.CS$&amp;lt;&amp;gt;9__CachedAnonymousMethodDelegate1 = delegate(TypeDefinition x)
  {
   IEnumerable&amp;lt;MethodDefinition&amp;gt; enumerable = x.Methods;
   return enumerable;
  }
  ;
 }
 Enumerable.ToList&amp;lt;MethodDefinition&amp;gt;(Enumerable.SelectMany&amp;lt;TypeDefinition, MethodDefinition&amp;gt;(arg_5F_0, Program.CS$&amp;lt;&amp;gt;9__CachedAnonymousMethodDelegate1)).ForEach(new Action&amp;lt;MethodDefinition&amp;gt;(Program.Decompile));
}&lt;/pre&gt;
&lt;p&gt;which leaks some of the internal plumbing -- delegate caching; how extension methods are actually handled -- but does at least deliver.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-7985170930916150187?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/7985170930916150187/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=7985170930916150187' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/7985170930916150187'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/7985170930916150187'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/02/ilspy-and-f.html' title='ILSpy and F#'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-7183541218626141674</id><published>2011-02-14T22:26:00.002Z</published><updated>2011-02-14T22:45:50.704Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='build'/><category scheme='http://www.blogger.com/atom/ns#' term='IronPython'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Scripting the Win32 API - F# and IronPython FFI</title><content type='html'>&lt;p&gt;Mainly a worked example for reminding me of how the syntax goes, reimplementing &lt;code&gt;sn -k&lt;/code&gt; in F# and IronPython:&lt;/p&gt;
&lt;pre class="brush: f#"&gt;open System
open System.Diagnostics
open System.IO
open System.Runtime.InteropServices

#nowarn "51"
// "The use of native pointers may result in unverifiable .NET IL code" --
// for arguments &amp;amp;&amp;amp;keyBlob, &amp;amp;&amp;amp;generatedSize in the call to StrongNameKeyGenEx

module NativeMethods =
    [&amp;lt;DllImport("mscoree.dll")&amp;gt;]
    extern int StrongNameErrorInfo()
    
    [&amp;lt;DllImport("mscoree.dll")&amp;gt;]
    extern bool StrongNameKeyGenEx(
                  [&amp;lt;MarshalAs(UnmanagedType.LPWStr)&amp;gt;]String wszKeyContainer,
                    int dwFlags, int dwKeySize, IntPtr* ppbKeyBlob, int* pcbKeyBlob)
                  
    [&amp;lt;DllImport("mscoree.dll")&amp;gt;]
    extern void StrongNameFreeBuffer(IntPtr pbMemory)

let GetLastStrongNameError() =
    Marshal.GetExceptionForHR(NativeMethods.StrongNameErrorInfo()).Message

let GenerateKeyPair (keyK : int) =
    if keyK &amp;lt; 0 || keyK &amp;gt; 2 then
        raise (new ArgumentOutOfRangeException("keyK", keyK, "Invalid Key Size -- should be 1 or 2"))
               
    // variables that hold the unmanaged key
    let mutable keyBlob = IntPtr.Zero
    let mutable generatedSize = 0

    // create the key
    let createdKey = NativeMethods.StrongNameKeyGenEx(
                                   null, 0, 1024 * keyK,
                                   &amp;amp;&amp;amp;keyBlob, &amp;amp;&amp;amp;generatedSize)
    try
       // if there was a problem, translate it and report it
       if not createdKey || keyBlob = IntPtr.Zero then
           raise (new InvalidOperationException(GetLastStrongNameError()))

       // make sure the key size makes sense
       if generatedSize &amp;lt;= 0 || generatedSize &amp;gt; Int32.MaxValue then
           raise (new InvalidOperationException("InternalError"))

       // get the key into managed memory
       let key = Array.create generatedSize 0uy
       Marshal.Copy(keyBlob, key, 0, generatedSize)
       key
    finally
       // release the unmanaged memory the key resides in
       if keyBlob &amp;lt;&amp;gt; IntPtr.Zero then
           NativeMethods.StrongNameFreeBuffer(keyBlob)
           
// path to generate new string name key
let keypath = fsi.CommandLineArgs.[1]

// write the key to the specified file if it is not already present
if not (File.Exists(keypath)) then
    let key = GenerateKeyPair 1
    use snkStream = new FileStream(keypath, FileMode.Create, FileAccess.Write)
    use snkWriter = new BinaryWriter(snkStream)
    snkWriter.Write(key)
  &lt;/pre&gt;
&lt;p&gt;This about halfway between how you'd just call the APIs naturally in C++/CLI, and the full process of C# P/Invoke.  You do need to do your own &lt;code&gt;extern&lt;/code&gt; method declarations, whereas IronPython is something else again:&lt;/p&gt;
&lt;pre class="brush: py"&gt;from System import Int32, Byte, IntPtr
import System
from System.IO import *
from System.Runtime.InteropServices import Marshal
from ctypes import *
import sys

mscoree = windll.mscoree

def GetLastStrongNameError():
    error = Marshal.GetExceptionForHR(mscoree.StrongNameErrorInfo())
    return error.Message

def GenerateKeyPair (keyK):
    if keyK &amp;lt; 0 or keyK &amp;gt; 2 :
        raise System.ArgumentOutOfRangeException(
        "keyK", keyK, "Invalid Key Size -- should be 1 or 2")
               
    # variables that hold the unmanaged key
    keyBlob = c_void_p()
    generatedSize = c_uint(0)

    # create the key
    createdKey = mscoree.StrongNameKeyGenEx(
                                   None, 
                                   c_uint(0),
                                   c_uint(1024 * keyK),
                                   byref(keyBlob),
                                   byref(generatedSize))

    try:
       # if there was a problem, translate it and report it
       if not createdKey or keyBlob.value == 0:
           raise  System.InvalidOperationException(GetLastStrongNameError())

       #make sure the key size makes sense
       generatedSize = generatedSize.value
       if generatedSize &amp;lt;= 0 or generatedSize &amp;gt; Int32.MaxValue:
           raise System.InvalidOperationException("InternalError")

       # get the key into managed memory
       key = System.Array.CreateInstance(Byte, generatedSize)
       Marshal.Copy(IntPtr(keyBlob.value), key, 0, generatedSize)
       return key
    finally:
       # release the unmanaged memory the key resides in
       if keyBlob.value != 0:
           mscoree.StrongNameFreeBuffer(keyBlob)
           
# write the key to the specified file if it is not already present
keypath = sys.argv[1]
if not (File.Exists(keypath)):
    print 'Generating key at %s' % (keypath)
    key = GenerateKeyPair (1)
    # should do this using a 'with'
    snkStream = FileStream(keypath, FileMode.Create, FileAccess.Write)
    snkWriter = BinaryWriter(snkStream)
    snkWriter.Write(key)
    snkWriter.Close()&lt;/pre&gt;
&lt;p&gt;where it was easier to fudge the pointer value for the key buffer into an &lt;code&gt;IntPtr&lt;/code&gt; than to try and dereference it via &lt;code&gt;ctypes&lt;/code&gt; and make a manual copy.&lt;/p&gt;
&lt;p&gt;The code here is an adapted subset of &lt;a href="http://blogs.msdn.com/b/shawnfa/archive/2004/07/09/178902.aspx"&gt;a managed API for strong-name keys&lt;/a&gt;; the use-case being a contingent key generation as part of a build process.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-7183541218626141674?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/7183541218626141674/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=7183541218626141674' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/7183541218626141674'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/7183541218626141674'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/02/scripting-win32-api-f-and-ironpython.html' title='Scripting the Win32 API - F# and IronPython FFI'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-581377518626901233</id><published>2011-02-12T23:52:00.004Z</published><updated>2011-02-13T00:00:20.423Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Mileage'/><category scheme='http://www.blogger.com/atom/ns#' term='weather'/><category scheme='http://www.blogger.com/atom/ns#' term='cycling'/><category scheme='http://www.blogger.com/atom/ns#' term='garden'/><title type='text'>Signs of Spring</title><content type='html'>&lt;p&gt;After the front-loaded winter in November and December, mild and often wet weather has brought the snowdrops to their height now, and in recent days crocuses and early irises too; even in sunny spots, early primroses, while the viburnum continues.&lt;/p&gt;
&lt;p&gt;I cycled into town today to give my newly rebuilt bike (a full 3 star service from &lt;a href="http://www.massifoutdoors.co.uk/"&gt;the new bike shop near work&lt;/a&gt;) a try-out; and having put on a winter-weight jacket was quite hot by the time I got there.  So I stuffed the jacket in one pannier and was nigh-on the only person around with T-shirt and shades -- heavy coats and jackets were everywhere.  Indeed so mild it was that I was comfortable cycling home without putting the jacket back on as the sun occasionally went behind the first real fair-weather cumulus I've spotted this year.&lt;/p&gt;
&lt;p&gt;Other notes -- 6 months with the new car and a whisker under 2200 miles done on it, allowing for the clock showing 14 when I took delivery (from parking at home a fortnight ago with exactly 2000 showing).&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-581377518626901233?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/581377518626901233/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=581377518626901233' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/581377518626901233'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/581377518626901233'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/02/signs-of-spring.html' title='Signs of Spring'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-2513911990738333356</id><published>2011-02-12T23:42:00.002Z</published><updated>2011-02-12T23:50:38.710Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='build'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>MSBuild -- getting the assemblies you depend on from other projects</title><content type='html'>&lt;p&gt;So in one project, you'd like to do something where you automatically pick up the other assemblies you've built in projects you depend on -- an obvious case in point being running some automated test based on the dependent projects.  Just going for the list of references will pick you up a whole raft of system assemblies that you can't get rid of in .net4; so you need to turn the project references into the output names.&lt;/p&gt;
&lt;p&gt;For that we have the &lt;code&gt;MSBuild&lt;/code&gt; task and the &lt;code&gt;GetTargetPath&lt;/code&gt; target, thus:&lt;/p&gt;
&lt;pre class="brush: xml"&gt;  &amp;lt;PropertyGroup&amp;gt;
    &amp;lt;TargetList&amp;gt;@(Targets-&amp;gt;'%(filename)%(extension)', '; ')&amp;lt;/TargetList&amp;gt;
  &amp;lt;/PropertyGroup&amp;gt;
  &amp;lt;Target Name="BeforeBuild"&amp;gt;
     &amp;lt;MSBuild
    Projects="@(ProjectReference)"
    Targets="GetTargetPath"&amp;gt;
    &amp;lt;Output TaskParameter="TargetOutputs" ItemName="Targets" /&amp;gt;
    &amp;lt;/MSBuild&amp;gt;
    &amp;lt;Message Text="My reference list is $(TargetList)" /&amp;gt;
  &amp;lt;/Target&amp;gt;&lt;/pre&gt;
&lt;p&gt;which is a trivial example where we just echo the values to the console, like&lt;/p&gt;
&lt;pre class="code"&gt;BeforeBuild:
  My reference list is AnotherAssembly.dll; NewAssembly.dll&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-2513911990738333356?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/2513911990738333356/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=2513911990738333356' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/2513911990738333356'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/2513911990738333356'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/02/msbuild-getting-assemblies-you-depend.html' title='MSBuild -- getting the assemblies you depend on from other projects'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-5008972898597744399</id><published>2011-02-11T00:08:00.002Z</published><updated>2011-02-11T00:18:08.643Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Getting the public key and token from a strong-name key</title><content type='html'>&lt;p&gt;I hope we're all familiar with &lt;code&gt;sn -Tp&lt;/code&gt; to get the public key details from an assembly -- here using an example where the key is public and anyone can reproduce the results:&lt;/p&gt;
&lt;pre class="code"&gt;&amp;gt;sn -Tp .\Mono.Cecil.dll

Microsoft (R) .NET Framework Strong Name Utility  Version 3.5.30729.1
Copyright (c) Microsoft Corporation.  All rights reserved.

Public key is
002400000480000094000000060200000024000052534131000400000100010079159977d2d03a
8e6bea7a2e74e8d1afcc93e8851974952bb480a12c9134474d04062447c37e0e68c080536fcf3c
3fbe2ff9c979ce998475e506e8ce82dd5b0f350dc10e93bf2eeecf874b24770c5081dbea7447fd
dafa277b22de47d6ffea449674a4f9fccf84d15069089380284dbdd35f46cdff12a1bd78e4ef00
65d016df

Public key token is 0738eb9f132ed756&lt;/pre&gt;
&lt;p&gt;But if you haven't built anything yet, you might still want these values from a string-name key pair (e.g. to put in an &lt;code&gt;InternalsVisibleTo&lt;/code&gt; attribute on the first assembly at the bottom of the calling stack for the unit tests that are to come).  So here's how&lt;/p&gt;
&lt;pre class="brush: f#"&gt;open System
open System.IO
open System.Reflection
open System.Security.Cryptography

let key = fsi.CommandLineArgs.[1]
let stream = new System.IO.FileStream(key, System.IO.FileMode.Open, System.IO.FileAccess.Read)
let pair = new StrongNameKeyPair(stream)

// get the public key token as 8 bytes from the end of a SHA1 hash of the key material
let hash = new System.Security.Cryptography.SHA1CryptoServiceProvider()
let token = hash.ComputeHash(pair.PublicKey)
                |&amp;gt; Array.rev
                |&amp;gt; Seq.take 8
                
  
let patchArray s =
  s
  |&amp;gt; Seq.map (fun (x:byte) -&amp;gt; x.ToString("X2").ToLower() )
  |&amp;gt; Seq.toList

let rec chunkArray (s:string list) =
  let chunk = 39
  s
  |&amp;gt; Seq.truncate chunk
  |&amp;gt; Seq.iter Console.Write
  Console.WriteLine(String.Empty)
  if chunk &amp;gt; (List.length s ) then ()
  else  s
        |&amp;gt; Seq.skip chunk
        |&amp;gt; Seq.toList
        |&amp;gt; chunkArray

let dumpArray s =
  s
  |&amp;gt; patchArray
  |&amp;gt; chunkArray 
  
Console.WriteLine("Public key is")
pair.PublicKey
|&amp;gt; dumpArray
Console.WriteLine(String.Empty)

Console.Write("Public key token is ")
token
|&amp;gt; dumpArray
Console.WriteLine(String.Empty)&lt;/pre&gt;
&lt;p&gt;so now we can do&lt;/p&gt;
&lt;pre class="code"&gt;&amp;gt;&amp;amp; 'C:\Program Files\FSharp-2.0.0.0\bin\fsi.exe' .\sntp.fsx .\mono.snk
Public key is
002400000480000094000000060200000024000052534131000400000100010079159977d2d03a
8e6bea7a2e74e8d1afcc93e8851974952bb480a12c9134474d04062447c37e0e68c080536fcf3c
3fbe2ff9c979ce998475e506e8ce82dd5b0f350dc10e93bf2eeecf874b24770c5081dbea7447fd
dafa277b22de47d6ffea449674a4f9fccf84d15069089380284dbdd35f46cdff12a1bd78e4ef00
65d016df

Public key token is 0738eb9f132ed756&lt;/pre&gt;
&lt;p&gt;and get the values we want.  As most of the time you'll want the public key as a continuous string, much of the format nonsense in the script can be dispensed with.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-5008972898597744399?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/5008972898597744399/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=5008972898597744399' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/5008972898597744399'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/5008972898597744399'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/02/getting-public-key-and-token-from.html' title='Getting the public key and token from a strong-name key'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-2621561139779613752</id><published>2011-02-10T07:00:00.004Z</published><updated>2011-02-10T08:14:46.835Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='mono'/><title type='text'>Cecil.Decompiler and F#</title><content type='html'>&lt;p&gt;In this new post-Reflector age, I thought I'd have a look-see how the main competition worked on F#.  So I whipped up a simple driver for &lt;code&gt;&lt;a href="https://github.com/mono/cecil/tree/master/decompiler/Cecil.Decompiler"&gt;Cecil.Decompiler&lt;/a&gt;&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="brush: c#"&gt;  private static void Decompile(MethodDefinition m)
  {
   try {
    Formatter.WriteMethodBody(Console.Out, m);

   var lang = CSharp.GetLanguage(CSharpVersion.V3);
   var writer = lang.GetWriter(new PlainTextFormatter(Console.Out));
   writer.Write(m);
   } catch (Exception e) {
    Console.WriteLine(m.FullName);
    Console.WriteLine(e.Message);
   }
  }
  
  private static void DumpAssembly(string path)
  {
   var assembly = AssemblyDefinition.ReadAssembly(path);
   var pdbpath = Path.ChangeExtension (path, ".pdb");
   var provider = new PdbReaderProvider();
            var reader = provider.GetSymbolReader(assembly.MainModule, pdbpath);
            assembly.MainModule.ReadSymbols(reader);

   assembly.MainModule.Types.SelectMany(x =&amp;gt; x.Methods).ToList().ForEach(Decompile);    
  }&lt;/pre&gt;
&lt;p&gt;and tried it on an assembly of some fairly simple F# -- a few extension methods for &lt;code&gt;Option&lt;/code&gt;&lt;/p&gt;
&lt;pre class="brush: f#"&gt;module Augment =   

  type Microsoft.FSharp.Core.Option&amp;lt;'T&amp;gt; with
      static member filter (f : 'T -&amp;gt; bool) (x : option&amp;lt;'T&amp;gt;) =
        match x with
        | Some v when f(v) -&amp;gt; Some v
        | _ -&amp;gt; None
        
      static member getOrElse (fallback : 'T) (x : option&amp;lt;'T&amp;gt;) = defaultArg x fallback

      static member select (f : 'T -&amp;gt; bool) (x : 'T) =
        if f(x) then Some x
        else None
        
      static member nullable (x : 'a when 'a : null) : option&amp;lt;'a&amp;gt; =
        if x &amp;lt;&amp;gt; null then Some x
        else None&lt;/pre&gt;

&lt;p&gt;and we get things like&lt;/p&gt;
&lt;pre class="brush: f#"&gt;public static FSharpOption&amp;lt;T&amp;gt; Option`1.select.Static(FSharpFunc&amp;lt;T, bool&amp;gt; f, T x)
{
        if (f.Invoke(x))
        {
        }
        return FSharpOption&amp;lt;T&amp;gt;.Some(x);
        return null;
}&lt;/pre&gt;
&lt;p&gt;This is better than Reflector was when I first tried it about 18 months ago -- F#'s cluster of branch instructions&lt;/p&gt;
&lt;pre class="brush: c#; highlight: [5, 6, 7]"&gt;        IL_0000: nop
        IL_0001: ldarg.0
        IL_0002: ldarg.1
        IL_0003: callvirt !1 Microsoft.FSharp.Core.FSharpFunc`2&amp;lt;T,System.Boolean&amp;gt;::Invoke(!0)
        IL_0008: brfalse.s IL_000c
        IL_000a: br.s IL_000e
        IL_000c: br.s IL_0015
        IL_000e: ldarg.1
        IL_000f: call Microsoft.FSharp.Core.FSharpOption`1&amp;lt;!0&amp;gt; Microsoft.FSharp.Core.FSharpOption`1&amp;lt;T&amp;gt;::Some(!0)
        IL_0014: ret
        IL_0015: ldnull
        IL_0016: ret&lt;/pre&gt;

&lt;p&gt;caused the old Reflector to just crash.&lt;/p&gt;
&lt;p&gt;F#'s habit of lacing in temporaries did flummox the decompilation a bit:&lt;/p&gt;
&lt;pre class="brush: c#"&gt;public static FSharpOption&amp;lt;T&amp;gt; Option`1.filter.Static(FSharpFunc&amp;lt;T, bool&amp;gt; f, FSharpOption&amp;lt;T&amp;gt; x)
{
        if (x)
        {
                FSharpOption&amp;lt;T&amp;gt;  = ;
                T v = .get_Value();
                if (f.Invoke(v))
                {
                }
                T v = .get_Value();
                return FSharpOption&amp;lt;T&amp;gt;.Some(v);
        }
        return null;
}&lt;/pre&gt;
&lt;p&gt;compared with the current Reflector's take of&lt;/p&gt;
&lt;pre class="brush: c#"&gt;public static FSharpOption&amp;lt;T&amp;gt; Option`1.filter.Static&amp;lt;T&amp;gt;(FSharpFunc&amp;lt;T, bool&amp;gt; f, FSharpOption&amp;lt;T&amp;gt; x)
{
    FSharpOption&amp;lt;T&amp;gt; option = x;
    if (option != null)
    {
        FSharpOption&amp;lt;T&amp;gt; option2 = option;
        T v = option2.get_Value();
        if (f.Invoke(v))
        {
            return FSharpOption&amp;lt;T&amp;gt;.Some(option2.get_Value());
        }
    }
    return null;
}&lt;/pre&gt;
&lt;p&gt;MonoDevelop 2.4.2.'s Assembly Browser gets it a bit better than the raw decompiler, though it still has the same 'C'-style &lt;code&gt;if&lt;/code&gt; in there:&lt;/p&gt;
&lt;pre class="brush: c#"&gt;public static FSharpOption&amp;lt;T&amp;gt; Option`1.filter.Static(FSharpFunc&amp;lt;T, bool&amp;gt; f, FSharpOption&amp;lt;T&amp;gt; x)
{
 if (x)
 {
  FSharpOption&amp;lt;T&amp;gt; V_1 = V_0;
  T V_2 = V_1.get_Value();
  if (f.Invoke(V_2))
  {
  }
  T V_3 = V_1.get_Value();
  return FSharpOption&amp;lt;T&amp;gt;.Some(V_3);
 }
 return null;
}&lt;/pre&gt;

&lt;p&gt;Amusingly, I was able to get the decompilation to throw by feeding the little driver program into itself -- the &lt;code&gt;DumpAssembly&lt;/code&gt; method would not turn back into C#!  MonoDevelop silently refused to load the contents of the whole assembly.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-2621561139779613752?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/2621561139779613752/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=2621561139779613752' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/2621561139779613752'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/2621561139779613752'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/02/cecildecompiler-and-f.html' title='Cecil.Decompiler and F#'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-2800645623734695317</id><published>2011-02-06T14:35:00.002Z</published><updated>2011-02-06T14:50:36.030Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='mono'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Getting the baked-in .pdb location from an assembly with Mono.Cecil 0.9.4</title><content type='html'>&lt;p&gt;Because it isn't always as simple as &lt;/p&gt;
&lt;pre class="brush: c#"&gt;  public static string GetPdbFileName (string assemblyFileName)
  {
   return Path.ChangeExtension (assemblyFileName, ".pdb");
  }
&lt;/pre&gt;
&lt;p&gt;as is done in &lt;code&gt;Mono.Cecil.Pdb.PdbHelper&lt;/code&gt;, if assemblies and symbols have been moved to separate locations during a build.&lt;/p&gt;
&lt;p&gt;The tools are there -- we just need to get the debug data from the PE image if it's present, skip the first 24 bytes, and interpret the rest as a string.  Alas, all the are annoyingly just slightly encapsulated from us.  But never mind!  Reflection gets us there without having to negotiate a patch or make a fork:&lt;/p&gt;
&lt;pre class="brush: c#"&gt;        /// &amp;lt;summary&amp;gt;
        /// Violate Cecil encapsulation to get the PDB path -- a candidate
        /// for another method on ModuleDefinition
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name="assembly"&amp;gt;The assembly to find the .pdb path for&amp;lt;/param&amp;gt;
        /// &amp;lt;returns&amp;gt;The path (null if the operation fails)&amp;lt;/returns&amp;gt;
        public static string GetPdbFromImage(AssemblyDefinition assembly)
        {
            var m = assembly.MainModule;
            var imageField = typeof(ModuleDefinition).GetField("Image", BindingFlags.Instance | BindingFlags.NonPublic);
            var image = imageField.GetValue(m);
            var getDebugHeaderInfo = image.GetType().GetMethod("GetDebugHeader");
            byte[] result = null;
            var args = new object[] { result };
    
            try 
            {
                getDebugHeaderInfo.Invoke(image, args);
                var SizeOfDebugInfo = 0x18;
                result = args[0] as byte[];
                if (null == result)
                {
                    return null;
                }

                var byteValue = result.Skip(SizeOfDebugInfo).
                    TakeWhile(x =&amp;gt; x != 0).ToArray();

                if (byteValue.Length &amp;gt; 0)
                {
                    // UTF-8 encoding works for an assembly named GetÞePdbLocation.
                    return Encoding.UTF8.GetString(byteValue);
                }
            }
            catch (TargetInvocationException)
            {}

            return null;
        }
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-2800645623734695317?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/2800645623734695317/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=2800645623734695317' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/2800645623734695317'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/2800645623734695317'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/02/getting-baked-in-pdb-location-from.html' title='Getting the baked-in .pdb location from an assembly with Mono.Cecil 0.9.4'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-2796923294905373382</id><published>2011-01-19T22:26:00.005Z</published><updated>2011-01-19T23:24:41.266Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='functional programming'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Monad.Zero -- doing it right</title><content type='html'>&lt;p&gt;For my own benefit (see title of blog) as much as anything else.  When &lt;a href="http://stevegilham.blogspot.com/2011/01/monadic-parsers-in-c.html"&gt;I wrote&lt;/a&gt; &lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Rule 1 of monadic parsers is -- if you return &lt;code&gt;Monad.Zero&lt;/code&gt;, you're probably doing it wrong... It's likely that you mean monadic return of an empty list.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;it's because there's a distinction between positively returning an empty result (i.e. successfully doing nothing) and returning nothing at all (not succeeding in what you were trying to do) -- as the F# documentation for &lt;code&gt;Zero&lt;/code&gt; puts it&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Called for empty else branches of if...then expressions in computation expressions.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;So, for a parser monad &lt;code&gt;Zero&lt;/code&gt; is the function that returns a parser that takes no input and always fails:&lt;/p&gt;
&lt;pre class="brush: c#"&gt;    public static Parser&amp;lt;TInput, TOutput&amp;gt; Zero&amp;lt;TInput, TOutput&amp;gt;()
    {
        Operation&amp;lt;TInput, TOutput&amp;gt; bound = cs =&amp;gt;
        {
            return new List&amp;lt;KeyValuePair&amp;lt;TOutput, IEnumerable&amp;lt;TInput&amp;gt;&amp;gt;&amp;gt;();
        };
        return bound.ToParser();
    }&lt;/pre&gt;
&lt;p&gt;for the &lt;code&gt;Enumerable&lt;/code&gt; or &lt;code&gt;Seq&lt;/code&gt; monad, it's the empty sequence, for &lt;code&gt;Maybe&lt;/code&gt; its &lt;code&gt;None&lt;/code&gt; and so forth -- basically the only thing you can return if you don't have any value to return.&lt;/p&gt;
&lt;p&gt;And for individual leaf parsers -- the functions that define an &lt;code&gt;Operation&amp;lt;TInput, TOutput&amp;gt;&lt;/code&gt; and return them wrapped as a &lt;code&gt;Parser&amp;lt;TInput, TOutput&amp;gt;&lt;/code&gt;, those &lt;code&gt;Operation&amp;lt;TInput, TOutput&amp;gt;&lt;/code&gt;s should return the same non-value when faced with a parse failure.&lt;/p&gt;
&lt;p&gt;By contrast, when composing a parser that may try to consume some of the input, but fail, and you want to not fail the whole parse, you need a parser that successfully matches nothing (and consumes nothing) as an alternative that provides a success value -- and that is not &lt;code&gt;Zero&lt;/code&gt;, but rather &lt;code&gt;Return&lt;/code&gt; of the non-value of the appropriate type.  So, for example, the parser combinator &lt;code&gt;Many&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="brush: c#"&gt;        public static Parser&amp;lt;TInput, List&amp;lt;TOutput&amp;gt;&amp;gt; Many&amp;lt;TInput, TOutput&amp;gt;(Parser&amp;lt;TInput, TOutput&amp;gt; p)
        {
            var arg1 = Many1(p);
            var arg2 = ParserMonad.Return&amp;lt;TInput, List&amp;lt;TOutput&amp;gt;&amp;gt;(new List&amp;lt;TOutput&amp;gt;());
            return Combinators.Oppp(arg1, arg2);
        }&lt;/pre&gt;
&lt;p&gt;is "Consume as many blocks of &lt;code&gt;p&lt;/code&gt; as you can -- or, if that fails, do nothing"; by contrast, the combinator &lt;code&gt;Sat&lt;/code&gt; fails if the predicate doesn't match:&lt;/p&gt;
&lt;pre class="brush: c#"&gt;        public static Parser&amp;lt;TInput, TOutput&amp;gt; Sat&amp;lt;TInput, TOutput&amp;gt;(Predicate&amp;lt;TInput&amp;gt; p, Func&amp;lt;TInput, TInput, TOutput&amp;gt; select)
        {
            return Item&amp;lt;TInput, TInput&amp;gt;(x =&amp;gt; x).SelectMany(c =&amp;gt;
            {
                if (p(c))
                    return ParserMonad.Return&amp;lt;TInput, TInput&amp;gt;(c);
                else
                    return ParserMonad.Zero&amp;lt;TInput, TInput&amp;gt;();
            }, select);
        }&lt;/pre&gt;
&lt;p&gt;So in that case it says &lt;code&gt;Zero&lt;/code&gt; and means &lt;code&gt;Zero&lt;/code&gt; because that's a failure to satisfy the parser -- here in the equivalent the F# computation expression we don't even have to write the &lt;code&gt;else&lt;/code&gt; branch; it's filled in for us by the compiler.&lt;/p&gt;
&lt;p&gt;And that's really the one subtlety to be borne in mind -- otherwise you end up with an almost intractable debugging issue when your parser fails unexpectedly, partway through consuming valid input.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-2796923294905373382?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/2796923294905373382/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=2796923294905373382' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/2796923294905373382'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/2796923294905373382'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/01/monadzero-doing-it-right.html' title='Monad.Zero -- doing it right'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-4194306575292806797</id><published>2011-01-18T20:02:00.003Z</published><updated>2011-01-18T20:17:00.914Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='functional programming'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Monadic Parsers in C#</title><content type='html'>&lt;p&gt;Updated 17-Jan -- because I realised I'd gotten Input and Output the wrong way around; too easy to do when you're using the same type in each case.&lt;/p&gt;
&lt;p&gt;Updated 18-Jan -- In which we bug-fix the heck out of the previous two iterations, and pretty much completely rewrite this post.  Rule 1 of monadic parsers is -- if you return Monad.Zero, you're probably doing it wrong...  It's likely that you mean monadic return of an empty list.&lt;/p&gt;

&lt;p&gt;Following on from Mike Hadlow's recent series of posts on &lt;a href="http://mikehadlow.blogspot.com/2011/01/monads-in-c-4-linq-loves-monads.html"&gt;monads in C#&lt;/a&gt;, and the "Aha!" moment that &lt;code&gt;SelectMany&lt;/code&gt; actually means &lt;code&gt;Bind&lt;/code&gt;, in the same way that &lt;code&gt;Select&lt;/code&gt; means &lt;code&gt;map&lt;/code&gt;, I thought I'd take it for a spin on something more than just the usual &lt;code&gt;Identity&lt;/code&gt;, &lt;code&gt;Maybe&lt;/code&gt; and &lt;code&gt;State&lt;/code&gt; monads.&lt;/p&gt;
&lt;p&gt;For a long while now there have been things like &lt;a href="http://fsharpcode.blogspot.com/2008/12/f-parser-monad-httpwww.html"&gt;illustrative F# ports&lt;/a&gt; of a &lt;a href="http://www.cs.nott.ac.uk/~gmh/pearl.pdf"&gt;Haskell monadic parser&lt;/a&gt; -- so why not, I thought, try to emulate that in C#?&lt;/p&gt;

&lt;p&gt;So we have a type which looks like this (abusing &lt;code&gt;KeyValuePair&lt;/code&gt; as a 2-tuple for .net 3.5 compatibility) -- &lt;p&gt;
&lt;pre class="brush: c#"&gt;    public delegate List&amp;lt;KeyValuePair&amp;lt;TOutput, IEnumerable&amp;lt;TInput&amp;gt;&amp;gt;&amp;gt; Operation&amp;lt;TInput, TOutput&amp;gt;(IEnumerable&amp;lt;TInput&amp;gt; seq);

    public struct Parser&amp;lt;TInput, TOutput&amp;gt;
    {
        public Operation&amp;lt;TInput, TOutput&amp;gt; Op;
    }&lt;/pre&gt;
&lt;p&gt;which is a monad upon the return type; the input stream parametrization being orthogonal to this.&lt;/p&gt;
&lt;p&gt;The monad itself (and here I've reverted to using &lt;code&gt;Bind&lt;/code&gt; for the simple case analogous to the F# case) looks like&lt;/p&gt;
&lt;pre class="brush: c#"&gt;    public static class ParserMonad
    {
        public static Parser&amp;lt;TInput, TOutput&amp;gt; ToParser&amp;lt;TInput, TOutput&amp;gt;(this Operation&amp;lt;TInput, TOutput&amp;gt; value)
        {
            return new Parser&amp;lt;TInput, TOutput&amp;gt; { Op = value };
        }

        public static Parser&amp;lt;TInput, UOutput&amp;gt; Bind&amp;lt;TInput, TOutput, UOutput&amp;gt;(
            this Parser&amp;lt;TInput, TOutput&amp;gt; value,
            Func&amp;lt;TOutput, Parser&amp;lt;TInput, UOutput&amp;gt;&amp;gt; operation)
        {
            Operation&amp;lt;TInput, UOutput&amp;gt; bound = cs =&amp;gt;
            {
                var r = value.Op(cs);
                var rr = r.Select(kv =&amp;gt; operation(kv.Key).Op(kv.Value));
                return rr.SelectMany(x =&amp;gt; x).ToList();
            };

            return bound.ToParser();
        }

        public static Parser&amp;lt;TInput, VOutput&amp;gt; SelectMany&amp;lt;TInput, TOutput, UOutput, VOutput&amp;gt;(this Parser&amp;lt;TInput, TOutput&amp;gt; value,
            Func&amp;lt;TOutput, Parser&amp;lt;TInput, UOutput&amp;gt;&amp;gt; operation,
            Func&amp;lt;TOutput, UOutput, VOutput&amp;gt; select)
        {
            return value.Bind(t =&amp;gt;
                    operation(t).Bind(u =&amp;gt; 
                        ParserMonad.Return&amp;lt;TInput, VOutput&amp;gt;(select(t, u))
                        ));
        }

        public static Parser&amp;lt;TInput, TOutput&amp;gt; Return&amp;lt;TInput, TOutput&amp;gt;(TOutput value)
        {
            Operation&amp;lt;TInput, TOutput&amp;gt; bound = cs =&amp;gt;
            {
                var tmp = new KeyValuePair&amp;lt;TOutput, IEnumerable&amp;lt;TInput&amp;gt;&amp;gt;(value, cs);
                return new List&amp;lt;KeyValuePair&amp;lt;TOutput, IEnumerable&amp;lt;TInput&amp;gt;&amp;gt;&amp;gt; { tmp };
            };
            return bound.ToParser();
        }

        public static Parser&amp;lt;TInput, TOutput&amp;gt; Zero&amp;lt;TInput, TOutput&amp;gt;()
        {
            Operation&amp;lt;TInput, TOutput&amp;gt; bound = cs =&amp;gt;
            {
                return new List&amp;lt;KeyValuePair&amp;lt;TOutput, IEnumerable&amp;lt;TInput&amp;gt;&amp;gt;&amp;gt;();
            };
            return bound.ToParser();
        }
    }&lt;/pre&gt;
&lt;p&gt;Here &lt;code&gt;ToParser&lt;/code&gt; is the convenience function-to-monad constructor; and &lt;code&gt;Return&lt;/code&gt; is what lifts an output value into the monad -- creating a parser that places that output value into the output stream.&lt;/p&gt;
&lt;p&gt;The combinators assemble all the parsers from the atomic units -- some here are more specialized than others. &lt;/p&gt;
&lt;pre class="brush: c#"&gt;    public static class Combinators
    {
        /// Non-deterministic choice -- The (++) operator
        /// is a (non-deterministic) choice operator for parsers. The parser p ++ q applies
        /// both parsers p and q to the argument string, and appends their list of results.
        public static Parser&amp;lt;TInput, TOutput&amp;gt; Opp&amp;lt;TInput, TOutput&amp;gt;(Parser&amp;lt;TInput, TOutput&amp;gt; p, Parser&amp;lt;TInput, TOutput&amp;gt; q)
        {
            Operation&amp;lt;TInput, TOutput&amp;gt; bound = cs =&amp;gt;
            {
                return p.Op(cs).Concat(q.Op(cs)).ToList();
            };
            return bound.ToParser();
        }

        /// a deterministic)choice operator (+++) that has
        /// the same behaviour as (++), except that at most one result is returned:
        public static Parser&amp;lt;TInput, TOutput&amp;gt; Oppp&amp;lt;TInput, TOutput&amp;gt;(Parser&amp;lt;TInput, TOutput&amp;gt; p, Parser&amp;lt;TInput, TOutput&amp;gt; q)
        {
            Operation&amp;lt;TInput, TOutput&amp;gt; bound = cs =&amp;gt;
            {
                var sum = Opp(p, q).Op(cs);
                if (sum.Any())
                {
                    return new List&amp;lt;KeyValuePair&amp;lt;TOutput, IEnumerable&amp;lt;TInput&amp;gt;&amp;gt;&amp;gt; { sum.First() };
                }

                return new List&amp;lt;KeyValuePair&amp;lt;TOutput, IEnumerable&amp;lt;TInput&amp;gt;&amp;gt;&amp;gt;();
            };

            return bound.ToParser();
        }

        /// Matches 1 or more applications of parser p
        /// f# prototype is parser['a, 'b] -&amp;gt; parser['a list, 'b]
        public static Parser&amp;lt;TInput, List&amp;lt;TOutput&amp;gt;&amp;gt; Many1&amp;lt;TInput, TOutput&amp;gt;(Parser&amp;lt;TInput, TOutput&amp;gt; p)
        {
            var parser = p.Bind(x =&amp;gt;
            {
                var manyp = Many(p);
                var outer = manyp.Bind(xs =&amp;gt;
                {
                    var tmp = new List&amp;lt;TOutput&amp;gt; { x };
                    var result = tmp.Concat(xs).ToList();
                    return ParserMonad.Return&amp;lt;TInput, List&amp;lt;TOutput&amp;gt;&amp;gt;(result);
                });
                return outer;
            });

            return parser;
        }

        /// Matches 0 or more applications of parser p
        /// f# prototype is parser['a, 'b] -&amp;gt; parser['a list, 'b]
        public static Parser&amp;lt;TInput, List&amp;lt;TOutput&amp;gt;&amp;gt; Many&amp;lt;TInput, TOutput&amp;gt;(Parser&amp;lt;TInput, TOutput&amp;gt; p)
        {
            var arg1 = Many1(p);
            var arg2 = ParserMonad.Return&amp;lt;TInput, List&amp;lt;TOutput&amp;gt;&amp;gt;(new List&amp;lt;TOutput&amp;gt;());
            return Combinators.Oppp(arg1, arg2);
        }

        /// Apply a parser
        public static Parser&amp;lt;TInput, TOutput&amp;gt; Apply&amp;lt;TInput, TOutput&amp;gt;(Parser&amp;lt;TInput, TOutput&amp;gt; p)
        {
            var parser = p.Bind(x =&amp;gt; ParserMonad.Return&amp;lt;TInput, TOutput&amp;gt;(x));
            return parser;
        }

        /// A parser which successfully consumes the first element
        /// of the input stream if it is is non-empty, and fails otherwise.
        /// f# prototype is (unit -&amp;gt;) parser[char, string]
        public static Parser&amp;lt;TInput, TOutput&amp;gt; Item&amp;lt;TInput, TOutput&amp;gt;(Func&amp;lt;TInput, TOutput&amp;gt; select)
        {
            Operation&amp;lt;TInput, TOutput&amp;gt; bound =
                    cs =&amp;gt;
                    {
                        if (cs.Any())
                        {
                            return new List&amp;lt;KeyValuePair&amp;lt;TOutput, IEnumerable&amp;lt;TInput&amp;gt;&amp;gt;&amp;gt; 
                            { 
                                new KeyValuePair&amp;lt;TOutput, IEnumerable&amp;lt;TInput&amp;gt;&amp;gt;(select(cs.First()), cs.Skip(1).ToList()) 
                            };
                        }

                        return new List&amp;lt;KeyValuePair&amp;lt;TOutput, IEnumerable&amp;lt;TInput&amp;gt;&amp;gt;&amp;gt;();
                    };

            return bound.ToParser();
        }

        /// A parser that consumes a single token if it matches the predicate
        /// or fails otherwise
        /// f# prototype is (char-&amp;gt;bool) -&amp;gt; parser[char, string]
        public static Parser&amp;lt;TInput, TOutput&amp;gt; Sat&amp;lt;TInput, TOutput&amp;gt;(Predicate&amp;lt;TInput&amp;gt; p, Func&amp;lt;TInput, TInput, TOutput&amp;gt; select)
        {
            var parser = Item&amp;lt;TInput, TInput&amp;gt;(x =&amp;gt; x).SelectMany(c =&amp;gt;
            {
                if (p(c))
                {
                    return ParserMonad.Return&amp;lt;TInput, TInput&amp;gt;(c);
                }
                else
                {
                    return ParserMonad.Zero&amp;lt;TInput, TInput&amp;gt;();
                }
            }, select);

            return parser;
        }

        /// A parser for specific characters
        /// f# prototype is char -&amp;gt; parser[char, string]
        public static Parser&amp;lt;char, TOutput&amp;gt; Char&amp;lt;TOutput&amp;gt;(char c, Func&amp;lt;char, char, TOutput&amp;gt; select)
        {
            return Sat(s =&amp;gt; s == c, select);
        }

        /// Parse a specific string
        /// f# prototype is string -&amp;gt; parser[char list, string]
        public static Parser&amp;lt;char, List&amp;lt;TOutput&amp;gt;&amp;gt; StringP&amp;lt;TOutput&amp;gt;(string input, Func&amp;lt;char, char, TOutput&amp;gt; select)
        {
            if (input.Any())
            {
                var parser = Char&amp;lt;TOutput&amp;gt;(input.ToCharArray()[0], select).Bind(c =&amp;gt;
                {
                    var p2 = StringP(input.Substring(1), select);
                    var inner = p2.Bind(cs =&amp;gt;
                    {
                        var tmp = new List&amp;lt;TOutput&amp;gt; { c };
                        var result = tmp.Concat(cs).ToList();
                        return ParserMonad.Return&amp;lt;char, List&amp;lt;TOutput&amp;gt;&amp;gt;(result);
                    });
                    return inner;
                });

                return parser;
            }
            else
            {
                return ParserMonad.Return&amp;lt;char, List&amp;lt;TOutput&amp;gt;&amp;gt;(new List&amp;lt;TOutput&amp;gt;());
            }
        }
           
        /// Parse a string of spaces.
        /// f# prototype is (unit -&amp;gt;) parser[char list, string]
        public static Parser&amp;lt;char, List&amp;lt;TOutput&amp;gt;&amp;gt; Space&amp;lt;TOutput&amp;gt;(Func&amp;lt;char, char, TOutput&amp;gt; select)
        {
            return Many(Sat(System.Char.IsWhiteSpace, select));
        }

        /// Parse a token using a parser p, throwing away any trailing space.
        /// f# prototype is parser['a, string] -&amp;gt; parser['a, string]
        public static Parser&amp;lt;char, TOutput&amp;gt; Token&amp;lt;TOutput&amp;gt;(Parser&amp;lt;char, TOutput&amp;gt; p, Func&amp;lt;char, char, TOutput&amp;gt; select)
        {
            var parser = p.Bind(x =&amp;gt;
                        Space(select).Bind(xs =&amp;gt;
                        {
                            return ParserMonad.Return&amp;lt;char, TOutput&amp;gt;(x); 
                        }
                ));
            return parser;
        }

        /// Parse a symbolic token:
        /// f# prototype is string -&amp;gt; parser[char list, string]
        public static Parser&amp;lt;char, List&amp;lt;TOutput&amp;gt;&amp;gt; Symbol&amp;lt;TOutput&amp;gt;(string symbol, Func&amp;lt;char, char, TOutput&amp;gt; select)
        {
            Parser&amp;lt;char, List&amp;lt;TOutput&amp;gt;&amp;gt; inner = StringP&amp;lt;TOutput&amp;gt;(symbol, select);
            return Token&amp;lt;List&amp;lt;TOutput&amp;gt;&amp;gt;(inner, (x, y) =&amp;gt; new List&amp;lt;TOutput&amp;gt; { select(x,y) } );
        }

        /// Parse repeated applications of a parser p, separated by applications of a parser
        /// op whose result value is an operator that is assumed to associate to the left,
        /// and which is used to combine the results from the p parsers.
        public static Parser&amp;lt;TInput, TOutput&amp;gt; Chain1&amp;lt;TInput, TOutput&amp;gt; (
            Parser&amp;lt;TInput, TOutput&amp;gt; p,
            Parser&amp;lt;TInput, Func&amp;lt;TOutput, TOutput, TOutput&amp;gt;&amp;gt; op)
        {
            return p.Bind(x =&amp;gt; Chain1Rest(x, p, op));
        }

        /// Helper method for Chain1
        public static Parser&amp;lt;TInput, TOutput&amp;gt; Chain1Rest&amp;lt;TInput, TOutput&amp;gt; (
            TOutput a,
            Parser&amp;lt;TInput, TOutput&amp;gt; p,
            Parser&amp;lt;TInput, Func&amp;lt;TOutput, TOutput, TOutput&amp;gt;&amp;gt; op)
        {
            var parser = op.Bind(f =&amp;gt; 
                p.Bind(b =&amp;gt;
                    Chain1Rest(f(a, b), p, op)
                ));
            var stop = ParserMonad.Return&amp;lt;TInput, TOutput&amp;gt;(a);

            return Oppp(parser, stop);
        }

//F# Monadic Parser - Calculator Example :))) 

// Grammar
//expr ::= expr addop term | term
//term ::= term mulop factor | factor
//factor ::= digit | ( expr )
//digit ::= 0 | 1 | : : : | 9
//addop ::= + | -
//mulop ::= * | /

        public static Parser&amp;lt;char, Func&amp;lt;double, double, double&amp;gt;&amp;gt; AddOp()
        {
            var plus = Symbol&amp;lt;char&amp;gt;("+", (x, y) =&amp;gt; x).Bind(x =&amp;gt;
                ParserMonad.Return&amp;lt;char, Func&amp;lt;double, double, double&amp;gt;&amp;gt;(
                (a, b) =&amp;gt; a + b));
            var minus = Symbol&amp;lt;char&amp;gt;("-", (x, y) =&amp;gt; x).Bind(x =&amp;gt;
                ParserMonad.Return&amp;lt;char, Func&amp;lt;double, double, double&amp;gt;&amp;gt;(
                (a, b) =&amp;gt; a - b));
            return Oppp(plus, minus);
        }

        public static Parser&amp;lt;char, Func&amp;lt;double, double, double&amp;gt;&amp;gt; MulOp()
        {
            var plus = Symbol&amp;lt;char&amp;gt;("*", (x, y) =&amp;gt; x).Bind(x =&amp;gt;
                ParserMonad.Return&amp;lt;char, Func&amp;lt;double, double, double&amp;gt;&amp;gt;(
                (a, b) =&amp;gt; a * b));
            var minus = Symbol&amp;lt;char&amp;gt;("/", (x, y) =&amp;gt; x).Bind(x =&amp;gt;
                ParserMonad.Return&amp;lt;char, Func&amp;lt;double, double, double&amp;gt;&amp;gt;(
                (a, b) =&amp;gt; a / b));
            return Oppp(plus, minus);
        }

        public static Parser&amp;lt;char, double&amp;gt; Digit()
        {
            var sat = Sat&amp;lt;char, char&amp;gt;(System.Char.IsDigit, (x, y) =&amp;gt; x);
            return Token&amp;lt;char&amp;gt; (sat, (x,y) =&amp;gt; x).Bind&amp;lt;char, char, double&amp;gt;
                (x =&amp;gt; ParserMonad.Return&amp;lt;char, double&amp;gt;((double)(x - '0')));
        }

        public static Parser&amp;lt;char, double&amp;gt; Expr()
        {
            return Chain1&amp;lt;char, double&amp;gt;(Term(), AddOp());
        }

        public static Parser&amp;lt;char, double&amp;gt; Term()
        {
            return Chain1&amp;lt;char, double&amp;gt;(Factor(), MulOp());
        }

        public static Parser&amp;lt;char, double&amp;gt; Factor()
        {
            var paren = Symbol("(", (x, y) =&amp;gt; x).
                        Bind(x =&amp;gt; Expr().
                        Bind(n =&amp;gt; Symbol(")", (x2, y2) =&amp;gt; x2).
                        Bind(z =&amp;gt; ParserMonad.Return&amp;lt;char, double&amp;gt;(n))));
            return Oppp(Digit(), paren);
        }
    }&lt;/pre&gt;
&lt;p&gt;And we can top this off with an example&lt;/p&gt;
&lt;pre class="brush: c#"&gt;        public static void Main()
        {
            var restToken = Combinators.Sat&amp;lt;string, int&amp;gt;(x =&amp;gt;
            {
                return x.Length &amp;gt; 2;
            },
            (x,y) =&amp;gt; {
                return Math.Max(x.Length, y.Length);
            });
            var tail = Combinators.Many(restToken);
            var data = new List&amp;lt;string&amp;gt; { "the", "quick", "brown", "fox" };

            var results = Combinators.Apply(tail).Op(data);

            if (results.Any())
            {
                results.ForEach(x =&amp;gt; Console.WriteLine(
                    "key={0}; value={1}",
                    "[" + string.Join("; ", x.Key.Select(n=&amp;gt;n.ToString()).ToArray()) + "]",
                    "[" + string.Join("; ", x.Value.ToArray()) + "]"));
            }
            else
            {
                Console.WriteLine("empty");
            }

            var calc = "5 * (3 + 4)";
            var summed = Combinators.Apply(Combinators.Expr()).Op(calc.ToCharArray());
            if (summed.Any())
            {
                summed.ForEach(x =&amp;gt; Console.WriteLine(
                    "key={0}; value={1}",
                    "[" + x.Key + "]",
                    "[" + string.Join("; ", x.Value.Select(z =&amp;gt; z.ToString()).ToArray()) + "]"));
            }
            else
            {
                Console.WriteLine("empty");
            }
        }&lt;/pre&gt;
&lt;p&gt;which produces a successfully parsed output&lt;/p&gt;
&lt;pre class="code"&gt;key=[3; 5; 5; 3]; value=[]
key=[35]; value=[]&lt;/pre&gt;
&lt;hr /&gt;
&lt;p&gt;So what lessons do I draw from this exercise?&lt;/p&gt;
&lt;p&gt;The big one is that old saying about needing to be twice as clever to debug code as to write it; and code that works with continuation passing can get pretty darn clever.  Or, &lt;a href="http://blogs.msdn.com/b/ericlippert/archive/2010/10/27/continuation-passing-style-revisited-part-five-cps-and-asynchrony.aspx"&gt;as Eric Lippert put it&lt;/a&gt; (in the context of writing asynchronous code in this style in C#):&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Remember what I was saying about the pros and cons of CPS?&lt;/p&gt;

&lt;ul&gt;&lt;li&gt; PRO: Arbitrarily complex and interesting control flows can be built out of simple parts – check.
&lt;/li&gt;&lt;li&gt; CON: The reification of control flow via continuations is hard to read and hard to reason about – check.
&lt;/li&gt;&lt;li&gt; CON: The code that represents the mechanisms of control flow completely overwhelms the meaning of the code – check.
&lt;/li&gt;&lt;li&gt; CON: The transformation of ordinary code control flow into CPS is the kind of thing that compilers are good at, and almost no one else is – check. &lt;/li&gt;&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;The parts are indeed individually simple enough; but all the rest is sadly true.  We can see how clever, what the actual power-to-weight ratio is, or how much what actually gets executed differs from what it superficially looks like we've written, by replacing the initialization in &lt;code&gt;ToParser&lt;/code&gt; with&lt;/p&gt;
&lt;pre class="brush: c#"&gt;{ Op = x =&gt; {
                max = Math.Max(max, new System.Diagnostics.StackTrace().GetFrames().Length);
                return value(x); 
            } }&lt;/pre&gt;
&lt;p&gt;for a static integer value &lt;code&gt;max&lt;/code&gt; in the class.  Running the test example, we see that the stack depths achieved by these simple parsers are 64 and 95 respectively -- causes and effects are remote from one another (with the side effect that the only code not covered in this belongs to the subtract and divide operator parsers, and the failure handlers in the main program.&lt;/p&gt;
&lt;p&gt;Coupled with this is the difficulty of doing anything meaningful in the way of fine-grained TDD in the bootstrap phase -- the individual pieces are not parsers, are comparatively trivial, and even those return functions operating on functions; it's in combination that they do their magic, and there's a lot of code needed just to read one token out of a stream.&lt;/p&gt;
&lt;p&gt;Having the F# equivalent to work with -- to provide guidance of what types all the individual pieces were, and then in essence manually de-sugaring the computation expression notation -- was invaluable.  But if you have the F# version, there is little need for the transposition except as a five-finger exercise like this.&lt;/p&gt;
&lt;p&gt;On the positive side, though, once you have the toolkit like this and it handles even the simple cases, that probably means that it is robust; and then it can pretty much be taken as a black box, for those times when you absolutely have to do it in C# rather than F#.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-4194306575292806797?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/4194306575292806797/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=4194306575292806797' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/4194306575292806797'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/4194306575292806797'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/01/monadic-parsers-in-c.html' title='Monadic Parsers in C#'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-1986383344747465601</id><published>2011-01-16T22:27:00.002Z</published><updated>2011-11-07T00:26:03.508Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='weather'/><category scheme='http://www.blogger.com/atom/ns#' term='garden'/><title type='text'>More Garden Rubbish</title><content type='html'>&lt;p&gt;Insanely mild weather today meant that when I slipped out in bare feet and T-shirt to check the greenhouse, I ended up doing a bit more pottering around in the garden to catch up with some of the tidying from the autumn tasks without feeling the need to wear more.&lt;/p&gt;&lt;p&gt;I did put on a shirt and sandals for the catch-up pruning of the apple trees and digging over the beds where the annual flowers and the broccoli, which didn't survive the harsh weather in Nov-Dec, had been.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-1986383344747465601?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/1986383344747465601/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=1986383344747465601' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/1986383344747465601'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/1986383344747465601'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/01/more-garden-rubbish.html' title='More Garden Rubbish'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-9063673004476337329</id><published>2011-01-11T23:00:00.004Z</published><updated>2011-01-11T23:54:11.913Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='MSFT utilities'/><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Using F# 2.0 Powerpack ArgParser from C# -- (ii)</title><content type='html'>&lt;p&gt;Having resolved the issue of why the "--" separator wasn't triggering (PowerShell was swallowing it before it got to my code) that was stumping me in &lt;a href="http://stevegilham.blogspot.com/2011/01/using-f-20-powerpack-argparser-from-c.html"&gt;the previous post&lt;/a&gt;, I put together a little fluent wrapper to make this a little easier to consume from C#.  The code that isolates the F# types looks like this:&lt;/p&gt;
&lt;pre class="brush: c#"&gt;namespace Tinesware.ArgParser
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using Microsoft.FSharp.Core;
    using Microsoft.FSharp.Text;

    /// &amp;lt;summary&amp;gt;
    /// Assembles the various command line options and then calls the parser
    /// &amp;lt;/summary&amp;gt;
    public class Options
    {
        /// &amp;lt;summary&amp;gt;
        /// Option values
        /// &amp;lt;/summary&amp;gt;
        private List&amp;lt;ArgInfo&amp;gt; options = new List&amp;lt;ArgInfo&amp;gt;();

        /// &amp;lt;summary&amp;gt;
        /// Callbacks for boolean values
        /// &amp;lt;/summary&amp;gt;
        private List&amp;lt;Flag&amp;gt; flags = new List&amp;lt;Flag&amp;gt;();

        /// &amp;lt;summary&amp;gt;
        /// Initializes a new instance of the Options class.
        /// &amp;lt;/summary&amp;gt;
        public Options()
        {
        }

        /// &amp;lt;summary&amp;gt;
        /// An option with no argument
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name="option"&amp;gt;The option string&amp;lt;/param&amp;gt;
        /// &amp;lt;param name="callback"&amp;gt;The callback for this option&amp;lt;/param&amp;gt;
        /// &amp;lt;param name="usage"&amp;gt;Usage comment&amp;lt;/param&amp;gt;
        /// &amp;lt;returns&amp;gt;This object (fluent interface)&amp;lt;/returns&amp;gt;
        public Options Unit(string option, Action callback, string usage)
        {
            var type = ArgType.Unit(FuncConvert.ToFSharpFunc&amp;lt;Unit&amp;gt;(x =&amp;gt; callback()));
            this.options.Add(new ArgInfo(option, type, usage));
            return this;
        }

        /// &amp;lt;summary&amp;gt;
        /// An option with a string argument
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name="option"&amp;gt;The option string&amp;lt;/param&amp;gt;
        /// &amp;lt;param name="callback"&amp;gt;The callback for this option&amp;lt;/param&amp;gt;
        /// &amp;lt;param name="usage"&amp;gt;Usage comment&amp;lt;/param&amp;gt;
        /// &amp;lt;returns&amp;gt;This object (fluent interface)&amp;lt;/returns&amp;gt;
        public Options String(string option, Action&amp;lt;string&amp;gt; callback, string usage)
        {
            var type = ArgType.String(FuncConvert.ToFSharpFunc&amp;lt;string&amp;gt;(callback));
            this.options.Add(new ArgInfo(option, type, usage));
            return this;
        }

        /// &amp;lt;summary&amp;gt;
        /// An option with an integer argument
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name="option"&amp;gt;The option string&amp;lt;/param&amp;gt;
        /// &amp;lt;param name="callback"&amp;gt;The callback for this option&amp;lt;/param&amp;gt;
        /// &amp;lt;param name="usage"&amp;gt;Usage comment&amp;lt;/param&amp;gt;
        /// &amp;lt;returns&amp;gt;This object (fluent interface)&amp;lt;/returns&amp;gt;
        public Options Int(string option, Action&amp;lt;int&amp;gt; callback, string usage)
        {
            var type = ArgType.Int(FuncConvert.ToFSharpFunc&amp;lt;int&amp;gt;(callback));
            this.options.Add(new ArgInfo(option, type, usage));
            return this;
        }

        /// &amp;lt;summary&amp;gt;
        /// An option with a double precision argument
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name="option"&amp;gt;The option string&amp;lt;/param&amp;gt;
        /// &amp;lt;param name="callback"&amp;gt;The callback for this option&amp;lt;/param&amp;gt;
        /// &amp;lt;param name="usage"&amp;gt;Usage comment&amp;lt;/param&amp;gt;
        /// &amp;lt;returns&amp;gt;This object (fluent interface)&amp;lt;/returns&amp;gt;
        public Options Float(string option, Action&amp;lt;double&amp;gt; callback, string usage)
        {
            var type = ArgType.Float(FuncConvert.ToFSharpFunc&amp;lt;double&amp;gt;(callback));
            this.options.Add(new ArgInfo(option, type, usage));
            return this;
        }

        /// &amp;lt;summary&amp;gt;
        /// An end-of-arguments argument, usually "--"
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name="option"&amp;gt;The option string, usually "--"&amp;lt;/param&amp;gt;
        /// &amp;lt;param name="callback"&amp;gt;The callback for the remaining arguments&amp;lt;/param&amp;gt;
        /// &amp;lt;param name="usage"&amp;gt;Usage comment&amp;lt;/param&amp;gt;
        /// &amp;lt;returns&amp;gt;This object (fluent interface)&amp;lt;/returns&amp;gt;
        public Options Rest(string option, Action&amp;lt;string&amp;gt; callback, string usage)
        {
            var type = ArgType.Rest(FuncConvert.ToFSharpFunc&amp;lt;string&amp;gt;(callback));
            this.options.Add(new ArgInfo(option, type, usage));
            return this;
        }

        /// &amp;lt;summary&amp;gt;
        /// An argument indicating true
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name="option"&amp;gt;The option string&amp;lt;/param&amp;gt;
        /// &amp;lt;param name="callback"&amp;gt;The callback for the remaining arguments&amp;lt;/param&amp;gt;
        /// &amp;lt;param name="usage"&amp;gt;Usage comment&amp;lt;/param&amp;gt;
        /// &amp;lt;returns&amp;gt;This object (fluent interface)&amp;lt;/returns&amp;gt;
        public Options Set(string option, Action&amp;lt;bool&amp;gt; callback, string usage)
        {
            var flag = new Flag 
            { 
                Reference = new FSharpRef&amp;lt;bool&amp;gt;(false), 
                Callback = callback 
            };

            var type = ArgType.Set(flag.Reference);
            this.options.Add(new ArgInfo(option, type, usage));
            this.flags.Add(flag);
            return this;
        }

        /// &amp;lt;summary&amp;gt;
        /// An argument indicating false
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name="option"&amp;gt;The option string&amp;lt;/param&amp;gt;
        /// &amp;lt;param name="callback"&amp;gt;The callback for the remaining arguments&amp;lt;/param&amp;gt;
        /// &amp;lt;param name="usage"&amp;gt;Usage comment&amp;lt;/param&amp;gt;
        /// &amp;lt;returns&amp;gt;This object (fluent interface)&amp;lt;/returns&amp;gt;
        public Options Clear(string option, Action&amp;lt;bool&amp;gt; callback, string usage)
        {
            var flag = new Flag 
            { 
                Reference = new FSharpRef&amp;lt;bool&amp;gt;(true), 
                Callback = callback 
            };

            var type = ArgType.Set(flag.Reference);
            this.options.Add(new ArgInfo(option, type, usage));
            this.flags.Add(flag);
            return this;
        }

        /// &amp;lt;summary&amp;gt;
        /// Parse the environment command line with no default handler or usage comment
        /// &amp;lt;/summary&amp;gt;
        public void Parse()
        {
            this.Parse(null, null);
        }

        /// &amp;lt;summary&amp;gt;
        /// Parse the environment command line with no default handler
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name="usage"&amp;gt;usage comment&amp;lt;/param&amp;gt;
        public void Parse(string usage)
        {
            this.Parse(null, usage);
        }

        /// &amp;lt;summary&amp;gt;
        /// Parse the environment command line with no usage comment
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name="defaultHandler"&amp;gt;default handler for unmatched arguments&amp;lt;/param&amp;gt;
        public void Parse(Action&amp;lt;string&amp;gt; defaultHandler)
        {
            this.Parse(defaultHandler, null);
        }

        /// &amp;lt;summary&amp;gt;
        /// Parse the environment command line
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name="defaultHandler"&amp;gt;default handler for unmatched arguments&amp;lt;/param&amp;gt;
        /// &amp;lt;param name="usage"&amp;gt;usage comment&amp;lt;/param&amp;gt;
        public void Parse(Action&amp;lt;string&amp;gt; defaultHandler, string usage)
        {
            var temp = usage ?? string.Empty;

            var theUsage = string.IsNullOrEmpty(temp.Trim()) ?
                FSharpOption&amp;lt;string&amp;gt;.None :
                new FSharpOption&amp;lt;string&amp;gt;(usage);

            var theDefault = defaultHandler == null ?
                FSharpOption&amp;lt;FSharpFunc&amp;lt;string, Unit&amp;gt;&amp;gt;.None :
                new FSharpOption&amp;lt;FSharpFunc&amp;lt;string, Unit&amp;gt;&amp;gt;(
                    FuncConvert.ToFSharpFunc&amp;lt;string&amp;gt;(defaultHandler));

            ArgParser.Parse(this.options, theDefault, theUsage);

            this.flags.ForEach(x =&amp;gt; x.Callback(x.Reference.Value));
        }

        /// &amp;lt;summary&amp;gt;
        /// Report the command usage
        /// &amp;lt;/summary&amp;gt;
        public void Usage()
        {
            this.Usage(null);
        }

        /// &amp;lt;summary&amp;gt;
        /// Report the command usage
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name="remark"&amp;gt;Initial comment&amp;lt;/param&amp;gt;
        public void Usage(string remark)
        {
            var temp = remark ?? string.Empty;

            var theUsage = string.IsNullOrEmpty(temp.Trim()) ?
                FSharpOption&amp;lt;string&amp;gt;.None :
                new FSharpOption&amp;lt;string&amp;gt;(remark);

            ArgParser.Usage(this.options, theUsage);
        }

        /// &amp;lt;summary&amp;gt;
        /// Binds a ref bool and a callback
        /// &amp;lt;/summary&amp;gt;
        private struct Flag
        {
            /// &amp;lt;summary&amp;gt;
            /// A reference cell with a boolean value
            /// &amp;lt;/summary&amp;gt;
            public FSharpRef&amp;lt;bool&amp;gt; Reference;

            /// &amp;lt;summary&amp;gt;
            /// A post-parse callback
            /// &amp;lt;/summary&amp;gt;
            public Action&amp;lt;bool&amp;gt; Callback;
        }
    }
}
&lt;/pre&gt;
&lt;p&gt;The calling code, for the same example as before looks like&lt;/p&gt;
&lt;pre class="brush: c#"&gt;namespace fsgetopt
{
    class Program
    {
        static void Main(string[] args)
        {
            Action&amp;lt;string&amp;gt; compile = (s =&amp;gt; Console.WriteLine("Compiling {0}...", s));
            var outputName = "a.out";
            var verbose = false;
            var warningLevel = 0;
            var rest = new List&amp;lt;string&amp;gt;();
          
            var options = new Tinesware.ArgParser.Options().
                String("-o",
                    x =&amp;gt; outputName = x,
                    "Name of the output").
                Set("-v",
                    x =&amp;gt; verbose = x,
                    "Display additional information").
                Int("--warn",
                    x =&amp;gt; warningLevel = x,
                    "Set warning level").
                Rest("--",
                    x =&amp;gt; rest.Add(x),
                    "Stop parsing command line");
          
            options.Parse(compile, "Usage options are:");
            
            Console.WriteLine("outputName = {0}", outputName);
            Console.WriteLine("Verbose = {0}", verbose.Value);
            Console.WriteLine("Warning level = {0}", warningLevel);
            Console.WriteLine("rest = {0}", rest.Count);
        }
    }
}
&lt;/pre&gt;
&lt;p&gt;which is much clearer.&lt;/p&gt;
&lt;p&gt;Now edited for FxCop.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-9063673004476337329?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/9063673004476337329/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=9063673004476337329' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/9063673004476337329'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/9063673004476337329'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/01/using-f-20-powerpack-argparser-from-c_11.html' title='Using F# 2.0 Powerpack ArgParser from C# -- (ii)'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-7783002180184967508</id><published>2011-01-10T22:02:00.004Z</published><updated>2011-01-10T22:18:07.893Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Mileage'/><category scheme='http://www.blogger.com/atom/ns#' term='winter'/><category scheme='http://www.blogger.com/atom/ns#' term='garden'/><title type='text'>Garden Rubbish and Meaningless Numbers</title><content type='html'>&lt;p&gt;You can tell quite how early -- late November -- the big freeze set in, as for the last couple of weekends I was doing things like raking leaves off the lawn, as well as other winter chores like pruning roses and clearing other dead foliage from expected dieback (enough to fill the green bin).  The cold was also enough that inside the greenhouse, even under a further bubble-wrap tent with a heater set to frost-free, some of the pelargoniums got scorched -- not something I've had happen before in a winterised greenhouse.&lt;/p&gt;
&lt;p&gt;In the weekend clearout, I got rid of the last tomato plants from the tent, harvesting one last tomato.  Sunday I baked a crumble and almost used up the last of the apples -- leaving two Charles Ross picked and packed remained in a box of fruit that had mostly spontaneously self-destructed; and a large windfall Bramley (we ate the former raw and baked the latter for dessert today).  Many of the picked-as-ripe Bramleys also self-destructed from within in the last couple of weeks; the windfalls were just as durable as the cosseted ones.&lt;/p&gt;
&lt;p&gt;And some numbers -- at 08:39-08:40 on 21-Oct-10, the digital clock and odometer coincided; as again, cheating at 09:50 BST on 2-Nov; not cheating at 17:43-40 on 6-Jan-11; 1000 miles on 4-Nov.  The bike read 1033.9 miles at the end of the year (and also on the 6 month mark of having the bike computer, 7-Jan).&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-7783002180184967508?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/7783002180184967508/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=7783002180184967508' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/7783002180184967508'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/7783002180184967508'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/01/garden-rubbish-and-meaningless-numbers.html' title='Garden Rubbish and Meaningless Numbers'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-9036765228716237457</id><published>2011-01-10T21:31:00.005Z</published><updated>2011-01-12T08:00:33.321Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='MSFT utilities'/><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Using F# 2.0 Powerpack ArgParser from C#</title><content type='html'>&lt;p&gt;This is an update of the technique mentioned in an &lt;a href="http://strangelights.com/blog/archive/2005/08/13/1210.aspx"&gt;antique post by Robert Pickering&lt;/a&gt; which was &lt;a href="http://laurent.le-brun.eu/site/index.php/2010/06/08/55-fsharp-getopt-to-parse-the-command-line"&gt;referenced by Laurent Le Brun&lt;/a&gt; last summer.  I've taken Laurent's example of F# usage and ported it to C# in the most direct fashion:&lt;/p&gt;
&lt;pre class="brush: c#"&gt;using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.FSharp.Core;
using Microsoft.FSharp.Text;

namespace fsgetopt
{
    class Program
    {
        static void Main(string[] args)
        {
            Action&amp;lt;string&amp;gt; compile = (s =&amp;gt; Console.WriteLine("Compiling {0}...", s));
            var outputName = "a.out";
            var verbose = new FSharpRef&amp;lt;bool&amp;gt;(false);
            var warningLevel = 0;
            ArgInfo[] specs = 
            {
                new ArgInfo("-o", 
                    ArgType.String (FuncConvert.ToFSharpFunc&amp;lt;string&amp;gt; (x =&amp;gt; outputName=x)), 
                    "Name of the output"),
                new ArgInfo("-v", 
                    ArgType.Set (verbose), 
                    "Display additional information"),
                new ArgInfo("--warn", 
                    ArgType.Int (FuncConvert.ToFSharpFunc&amp;lt;int&amp;gt; (x =&amp;gt; warningLevel=x)), 
                    "Set warning level"),
                new ArgInfo("--", 
                    ArgType.Rest (FuncConvert.ToFSharpFunc&amp;lt;string&amp;gt; (x =&amp;gt; Console.WriteLine("rest has {0}", x))), 
                    "Stop parsing command line"),
            };

            var after = FuncConvert.ToFSharpFunc&amp;lt;string&amp;gt;(compile);
            var opt1 = new FSharpOption&amp;lt;FSharpFunc&amp;lt;string, Unit&amp;gt;&amp;gt;(after);
            var opt2 = new FSharpOption&amp;lt;string&amp;gt;("Usage options are:");
            ArgParser.Parse(specs, opt1, opt2);

            Console.WriteLine("outputName = {0}", outputName);
            Console.WriteLine("Verbose = {0}", verbose.Value);
            Console.WriteLine("Warning level = {0}", warningLevel);
        }
    }
}
&lt;/pre&gt;
&lt;p&gt;It is &lt;a href="http://stevegilham.blogspot.com/2011/01/using-f-20-powerpack-argparser-from-c_11.html"&gt;possible to streamline this&lt;/a&gt; to hide most of the messy type names inside utilities.&lt;/p&gt;
&lt;p&gt;One thing I've not managed to do is get the lambda associated with the &lt;code&gt;ArgType.Rest&lt;/code&gt; to fire, even after setting the default handler &lt;code&gt;opt1&lt;/code&gt; to &lt;code&gt;FSharpOption&amp;lt;T&amp;gt;.None&lt;/code&gt;.  That -- the default handler -- gets fired every time an otherwise unmatched argument is encountered; the &lt;code&gt;ArgType.Rest&lt;/code&gt; handler looks from the code like it should "just work" so I'm a bit baffled ATM. &lt;/p&gt;
&lt;h4&gt;LATER: Mystery solved&lt;/h4&gt;
&lt;p&gt;Of course, I was testing this at a PowerShell prompt -- and the shell was swallowing the unquoted "--" string : there was no "rest" to operate on.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-9036765228716237457?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/9036765228716237457/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=9036765228716237457' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/9036765228716237457'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/9036765228716237457'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2011/01/using-f-20-powerpack-argparser-from-c.html' title='Using F# 2.0 Powerpack ArgParser from C#'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-1586593891075733864</id><published>2010-12-29T17:22:00.003Z</published><updated>2010-12-29T18:30:10.459Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Film'/><category scheme='http://www.blogger.com/atom/ns#' term='book'/><category scheme='http://www.blogger.com/atom/ns#' term='anime'/><title type='text'>2010 in media</title><content type='html'>&lt;h4&gt;Film&lt;/h4&gt;
&lt;p&gt;A very disappointing year, with a totally dismal Film Festival, and otherwie only the Chaumet/Tati &lt;a href="http://stevegilham.blogspot.com/2010/09/film-lillusioniste.html"&gt;&lt;cite&gt;L'Illusioniste&lt;/cite&gt;&lt;/a&gt; really standing out enough to tempt me to the cinema. I would have gone to see &lt;cite&gt;The Story of Kells&lt;/cite&gt; as part of the festival had it not been in a no-unaccompanied-adult showing; but wasn't motivated to see it when it came around later.  And similarly, previous commitments and bad weather relieved me of having to decide whether to make the effort to fit in &lt;cite&gt;Rare Exports&lt;/cite&gt;.&lt;/p&gt;
&lt;h4&gt;Books&lt;/h4&gt;
&lt;p&gt;I actually read a few this year that weren't software related.  Excluding the holiday reading potboilers (Lindsey Davis' latest Falco, some by the numbers sci-fi,...)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Mi&amp;eacute;ville's &lt;cite&gt;Un Lun Dun&lt;/cite&gt; was a young people's &lt;cite&gt;Neverwhere&lt;/cite&gt; -- some nice ideas, but it didn't quite cohere in the end.  And I'm sufficiently bourgeois that the mundane London in the book was a more alien world than Un Lun Dun itself.&lt;/li&gt;
&lt;li&gt;Also by Mi&amp;eacute;ville, &lt;cite&gt;The City and the City&lt;/cite&gt; is the first Hugo winner I've read before the award since &lt;cite&gt;A Fire Upon The Deep&lt;/cite&gt;.  An interesting conceit of cities wrapped around a police procedural.&lt;/li&gt;
&lt;li&gt;Reynold's &lt;cite&gt;Terminal World&lt;/cite&gt; was a vast improvement over his previous &lt;cite&gt;&lt;a href="http://stevegilham.blogspot.com/2009/09/book-house-of-suns-by-alastair-reynolds.html"&gt;House of Suns&lt;/a&gt;&lt;/cite&gt;, being a fairly straightforward adventure yarn in a setting where Vingean Zones of Thought happen on a scale of miles rather than kiloparsecs.&lt;/li&gt;
&lt;li&gt;I also finally got around to Nahoko Uehashi's &lt;cite&gt;Moribito: Guardian of the Spirit&lt;/cite&gt; -- and was surprised quite how much &lt;a href="http://stevegilham.blogspot.com/2007/10/anime-seire-no-moribito.html"&gt;the anime&lt;/a&gt; had expanded upon the rather short and sparse narrative.&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;And while I'm here, probably worth warning people about the two weakest of the computing texts I picked up this year, both in the Wrox Professional series:&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;cite&gt;Professional Iron Python&lt;/cite&gt; was a very strange book, and it did cover some obscure points of .net/Windows programming; but for IronPython, not good.  It read like a draft that had been put together while the language was still in its pre-beta stages and then hurriedly given a Visual Studio 2010/.net 4 gloss.  In particular the author repeatedly makes the incorrect assertion that IronPython statements cannot span multiple lines -- which leaves me dubious as to the rest of the content I've not independently verified.  Get &lt;cite&gt;&lt;a href="http://stevegilham.blogspot.com/2009/05/book-ironpython-in-action-by-foord.html"&gt;IronPython in Action&lt;/a&gt;&lt;/cite&gt; instead.&lt;/li&gt;
&lt;li&gt;&lt;cite&gt;Professional F#2.0&lt;/cite&gt; just approaches being content free.  It covers very little of the language (no computation expressions, let alone anything like quotations), giving more emphasis to the imperative rather than functional aspects where it does.  Get the O'Reilly &lt;cite&gt;&lt;a href="http://oreilly.com/catalog/9780596153656"&gt;Programming F#&lt;/a&gt;&lt;/cite&gt; instead.&lt;/li&gt;&lt;/ul&gt;
&lt;h4&gt;Anime&lt;/h4&gt;
&lt;p&gt;2010 was the year where Crunchyroll reached a sufficient point that -- whatever the sordid history of the operation might be -- it was possible to watch a fair selection of series without resorting to fansubs.  A few series that I might have watched, or at least sampled (&lt;cite&gt;House of Five Leaves&lt;/cite&gt;, &lt;cite&gt;Tatami Galaxy&lt;/cite&gt;) were streamed by Funimation -- but they don't seem to have cottoned on that the world is more than just Japan and the US.&lt;/p&gt;
&lt;p&gt;Going by the metric that "/a/ doesn't talk about good anime" -- where it is obvious whether a title that is not talked about is good or bad -- &lt;cite&gt;House of Five Leaves&lt;/cite&gt;, from a manga by the same author as &lt;cite&gt;&lt;a href="http://stevegilham.blogspot.com/2009/07/anime-ristorante-paradiso.html"&gt;Ristorante Paradiso&lt;/a&gt;&lt;/cite&gt; is the only one that I feel any regrets about missing.&lt;/p&gt;
&lt;p&gt;Apart from titles already blogged, I still have the last episode of &lt;cite&gt;&lt;a href="http://en.wikipedia.org/wiki/Otome_Y%C5%8Dkai_Zakuro"&gt;Virgin Pomegranate Monster&lt;/a&gt;&lt;/cite&gt; to watch, as well as the concluding cours for &lt;cite&gt;Letter Bee REVERSE&lt;/cite&gt; and &lt;cite&gt;Super Robot Wars OG : The Inspector&lt;/cite&gt;.&lt;/p&gt;
&lt;p&gt;And of course no discussion of 2010 in anime would be complete without a mention of the new GAINAX series, &lt;cite&gt;Panty &amp; Stocking with Garterbelt&lt;/cite&gt;, a title that I watched just enough of to feel I could add it to my MAL dropped list (i.e. more than one episode).&lt;/p&gt;
&lt;p&gt;A combination of all that is wrong with American cartoons multiplied by "LOL, Japan!", this was the disappointing result of giving a studio of talented animators full freedom to have fun and do what they wanted.  Alas, what they wanted to do was act like retarded 12-year-olds -- so if you've grown out of finding messy bodily functions funny, or haven't seen any of the stuff that they're lampooning, and don't get your jollies from hearing cute Japanese girls swearing a blue streak, it ends up by being the saddest waste of talent I've seen in many a year.  And another case where I ignored the implication "it's being talked about, ergo it isn't very good", because I couldn't believe it was really that bad.&lt;/p&gt;
&lt;p&gt;Alas, it was.  In fact, watching the audience reactions as they were trolled mightily at every turn with misleading hints about forthcoming episodes, and desperately tried to make it all make sense, all the way up to the sudden ending with "To be continued in next season" (when Gainax have never done a second season of any of their original anime) was far, far more entertaining than the series itself.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-1586593891075733864?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/1586593891075733864/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=1586593891075733864' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/1586593891075733864'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/1586593891075733864'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2010/12/2010-in-media.html' title='2010 in media'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-8548231442333141253</id><published>2010-12-28T20:40:00.004Z</published><updated>2010-12-28T21:41:30.788Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='erlang'/><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><title type='text'>“Hello, OTP!” from F# revisited</title><content type='html'>&lt;p&gt;That &lt;a href="http://stevegilham.blogspot.com/2010/12/otp-from-f.html"&gt;previous F# code&lt;/a&gt; was just too ugly to leave as it was; so after a little bit of work, I've factored out a general purpose wrapper to convert the &lt;code&gt;Otp.Erlang&lt;/code&gt; types into values of a discriminated union on the F# side of the fence.  Like this we can directly pattern match, without the descent into massive nesting that we had before, and keep the &lt;code&gt;Otp.Erlang&lt;/code&gt; types encapsulated into wrappers for method calls on the &lt;code&gt;Otp&lt;/code&gt; types.

&lt;p&gt;With this addition the main program above can be revised to become&lt;/p&gt;
&lt;pre class="brush: f#"&gt;namespace ErsharpClient

open System
open Otp
open Tinesware.Unfurling

module Main =
         
  let ConnectTo serverNode cookie =
    let clientNode = new OtpSelf("clientnode", cookie)
    let serverNode = new OtpPeer(serverNode)
    clientNode.connect(serverNode)
    
  let ReceiveRPC (connection : OtpConnection) =
    Wrapper.ToTerm &amp;lt;| connection.receiveRPC()
    
  let MakeRPC (connection : OtpConnection) (moduleName:string) (functionName:string) (arguments: Term seq) =
     connection.sendRPC(moduleName, functionName, arguments |&amp;gt; Seq.map Wrapper.ToOtpObject |&amp;gt; Seq.toArray)
     ReceiveRPC connection
    
  let StartGenServer (connection : OtpConnection)  =
    match MakeRPC connection "mathserver" "start_link" [] with
    | Term.Tuple [ Term.Atom "ok" ; pid ] -&amp;gt; pid
    | Term.Tuple [ Term.Atom "error"; Term.Tuple [ Term.Atom "already_started" ; pid2 ]] -&amp;gt; pid2
    | start -&amp;gt; raise (new System.InvalidOperationException(start.ToString()))
    
  [&amp;lt;EntryPoint&amp;gt;]
  let Main arguments =
    let connection = ConnectTo "servernode@YourNodeNameHere" "cookie"
    let pid = StartGenServer connection
    
    let args = [6I; 9I]
               |&amp;gt; Seq.map Term.Integer
    
    match MakeRPC connection "mathserver" "multiply" args with
    | Term.Tuple [ Term.Atom "ok" ; Term.Integer value ] -&amp;gt; 
      Console.WriteLine("Return Value:" + value.ToString())
    | result -&amp;gt; Console.WriteLine("Failure:" + result.ToString())
    
    0
&lt;/pre&gt;
&lt;p&gt;which looks a lot more like the language we're trying to write in.  All the magic happens in this file&lt;/p&gt;
&lt;pre class="brush: f#"&gt;namespace Tinesware.Unfurling

open Otp

type public Pid = { Node     : string;
                    Id       : int;
                    Serial   : int;
                    Creation : int }
             
type public Port = { Node     : string;
                     Id       : int;
                     Creation : int }
              
type public Ref = { Node     : string;
                    Creation : int;
                    Ids      : int list }             
              
type public Term =
    | Double of float
    | Integer of bigint
    | List of Term list
    | Tuple of Term list
    | Atom of string
    | Binary of byte[]
    | Pid of Pid
    | Port of Port
    | Ref of Ref

module Wrapper =
    
    let rec ToTerm (term:Erlang.Object) =
        match term with
        | :? Erlang.Atom as atom -&amp;gt; Term.Atom &amp;lt;| atom.atomValue()
        | :? Erlang.Binary as binary -&amp;gt; Term.Binary &amp;lt;| binary.binaryValue()
        | :? Erlang.Double as double -&amp;gt; Term.Double &amp;lt;| double.doubleValue()
        | :? Erlang.List as list -&amp;gt; 
          let contents = list.elements()
                         |&amp;gt; Seq.map ToTerm
                         |&amp;gt; Seq.toList
          Term.List contents
        | :? Erlang.Long as long -&amp;gt; Term.Integer &amp;lt;| bigint (long.longValue())
        | :? Erlang.Pid as pid -&amp;gt; Term.Pid {Node = pid.node(); Id = pid.id(); Serial = pid.serial(); Creation = pid.creation()}
        | :? Erlang.Port as port -&amp;gt; Term.Port {Node = port.node(); Id = port.id(); Creation = port.creation()}
        | :? Erlang.Ref as ref -&amp;gt; Term.Ref {Node = ref.node(); Creation = ref.creation(); Ids =  ref.ids() |&amp;gt; Array.toList }
        | :? Erlang.Tuple as tuple -&amp;gt;
          let contents = tuple.elements()
                         |&amp;gt; Seq.map ToTerm
                         |&amp;gt; Seq.toList
          Term.Tuple contents
          
        | _ -&amp;gt; raise &amp;lt;| new System.InvalidOperationException(term.ToString())
        
    let rec ToOtpObject (term : Term) =
      match term with
      | Term.Atom atom -&amp;gt; new Erlang.Atom(atom) :&amp;gt; Erlang.Object
      | Term.Binary binary -&amp;gt; new Erlang.Binary(binary) :&amp;gt; Erlang.Object
      | Term.Double double -&amp;gt; new Erlang.Double(double) :&amp;gt; Erlang.Object
      | Term.Integer long -&amp;gt; new Erlang.Long(int64 long) :&amp;gt; Erlang.Object
      | Term.List list -&amp;gt; new Erlang.List( list 
                                           |&amp;gt; Seq.map ToOtpObject
                                           |&amp;gt; Seq.toArray ) :&amp;gt; Erlang.Object
      | Term.Pid pid -&amp;gt; new Erlang.Pid(pid.Node, pid.Id, pid.Serial, pid.Creation) :&amp;gt; Erlang.Object
      | Term.Port port -&amp;gt; new Erlang.Port(port.Node, port.Id, port.Creation) :&amp;gt; Erlang.Object
      | Term.Ref ref -&amp;gt; new Erlang.Ref(ref.Node, ref.Ids |&amp;gt; List.toArray, ref.Creation) :&amp;gt; Erlang.Object
      | Term.Tuple tuple -&amp;gt; new Erlang.Tuple ( tuple
                                           |&amp;gt; Seq.map ToOtpObject
                                           |&amp;gt; Seq.toArray ) :&amp;gt; Erlang.Object
&lt;/pre&gt;
&lt;p&gt;Of course, as OTP.Net is some revs behind the current Erlang distro's JInterface, this type mapping is incomplete (no &lt;code&gt;BitStr&lt;/code&gt; or &lt;code&gt;Fun&lt;/code&gt; types) compared with what we could be using.  The main advantage is that it works right now.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-8548231442333141253?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/8548231442333141253/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=8548231442333141253' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/8548231442333141253'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/8548231442333141253'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2010/12/otp-from-f-revisited.html' title='&amp;#8220;Hello, OTP!&amp;#8221; from F# revisited'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-1892598638117563688</id><published>2010-12-28T16:14:00.002Z</published><updated>2010-12-28T17:43:44.582Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='erlang'/><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><title type='text'>“Hello, OTP!” from F#</title><content type='html'>&lt;p&gt;Nothing earth-shattering here, just recording the results of a little bit of playing around with OTP.Net and F#.  This is all building on the original work done elsewhere &lt;a href="http://weblogs.asp.net/nleghari/archive/2008/01/08/integrating-net-and-erlang-using-otp-net.aspx"&gt;Integrating .NET and Erlang using OTP.NET&lt;/a&gt; and &lt;a href="http://bloggemdano.blogspot.com/2010/05/integrating-f-and-erlang-using-otpnet.html"&gt;Integrating F# and Erlang Using OTP.NET&lt;/a&gt;, as a little bit of a five-finger exercise using the OTP framework as now explained in &lt;a href="http://www.manning.com/logan/"&gt;&lt;cite&gt;Erlang and OTP in Action&lt;/cite&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So, simple &lt;code&gt;gen_server&lt;/code&gt; to export the same sort of API as the mathserver example in the former:&lt;/p&gt;
&lt;pre class="brush: erlang"&gt;%% ---------------------------------------------------------------------
%% File: mathserver.erl
%%
%% This is a simple implementation of a gen_server callback module.

-module(mathserver).

-behaviour(gen_server).

-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
         terminate/2, code_change/3]).
         
-export([
            start_link/0,
            stop/0,
            multiply/2
            ]).

-define(SERVER, ?MODULE).

-record(state, { }).
%%%===================================================================
%%% API
%%%===================================================================


%%--------------------------------------------------------------------
%% @doc Starts the server.
%%
%% @spec start_link() -&amp;gt; {ok, Pid}
%% where
%% Pid = pid()
%% @end
%%--------------------------------------------------------------------
start_link() -&amp;gt;
    gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
    
%%--------------------------------------------------------------------
%% @doc Stops the server.
%% @spec stop() -&amp;gt; ok
%% @end
%%--------------------------------------------------------------------
stop() -&amp;gt;
    gen_server:cast(?SERVER, stop).

%%--------------------------------------------------------------------
%% @doc multiplies two integers.
%% @spec multiply(X::integer(), Y::integer()) -&amp;gt; {ok, Product}
%% where
%% Product = integer()
%% @end
%%--------------------------------------------------------------------
multiply(X, Y) -&amp;gt;
    gen_server:call(?SERVER, {multiply, {X, Y}}).


%%%===================================================================
%%% gen_server callbacks
%%%===================================================================


init([]) -&amp;gt;
    {ok, #state{}}.

handle_call({multiply, {X, Y}}, _From, State) -&amp;gt;
    Reply = {ok, multiply_impl(X,Y)},
    {reply, Reply, State};
handle_call(_Request, _From, State) -&amp;gt;
    Reply = ok,
    {reply, Reply, State}.

handle_cast(_Msg, State) -&amp;gt;
    {noreply, State}.

handle_info(_Info, State) -&amp;gt;
    {noreply, State}.

terminate(_Reason, _State) -&amp;gt;
    ok.

code_change(_OldVsn, State, _Extra) -&amp;gt;
    {ok, State}.

%%%===================================================================
%%% API internals
%%%===================================================================

multiply_impl(First, Second) -&amp;gt;
    First * Second.&lt;/pre&gt;
&lt;p&gt;Compile this using &lt;code&gt;erlc&lt;/code&gt; and start &lt;code&gt;werl  -sname servernode -setcookie cookie&lt;/code&gt;.  Meanwhile the matching F# program to build and run is&lt;/p&gt;
&lt;pre class="brush: f#"&gt;namespace ErsharpClient

open System
open Otp

module Main =

  let (|Atom|_|) text (term : Erlang.Object) =
   match term with
   | :? Erlang.Tuple as tuple -&amp;gt;
    let terms = tuple.elements()
    if terms.Length &amp;lt;&amp;gt; 2 then
      None
    else match terms.[0] with
         | :? Erlang.Atom as atom -&amp;gt;
           if atom.atomValue() = text then Some(terms.[1])
           else None
         | _ -&amp;gt; None
   | _ -&amp;gt; None
         
  let ConnectTo serverNode cookie =
    let clientNode = new OtpSelf("clientnode", cookie)
    let serverNode = new OtpPeer(serverNode)
    clientNode.connect(serverNode)
    
  let StartGenServer (connection : OtpConnection)  =
    connection.sendRPC("mathserver", "start_link", Array.empty&amp;lt;Otp.Erlang.Object&amp;gt;)
    let start = connection.receiveRPC()
    match start with
    | Atom "ok" pid -&amp;gt; pid
    | Atom "error" value -&amp;gt; 
      match value with
      | Atom "already_started" pid2 -&amp;gt; pid2
      | _ -&amp;gt; raise (new System.InvalidOperationException(start.ToString()))
    | _ -&amp;gt; raise (new System.InvalidOperationException(start.ToString()))
    

  [&amp;lt;EntryPoint&amp;gt;]
  let Main arguments =
    let connection = ConnectTo "servernode@YourNodeNameHere" "cookie"
    let pid = StartGenServer connection
    
    let args = [| new Otp.Erlang.Long(6L); new Otp.Erlang.Long(9L)|] :  Otp.Erlang.Object array
    connection.sendRPC("mathserver", "multiply", args);
    let result = connection.receiveRPC()
    
    match result with
    | Atom "ok" value -&amp;gt; Console.WriteLine("Return Value:" + value.ToString())
    | _ -&amp;gt; Console.WriteLine("Failure:" + result.ToString())
    
    0
&lt;/pre&gt;
&lt;p&gt;Matching the dynamic types possible with Erlang, and the F# way with type hierarchies, makes this code somewhat ugly (as in multiply nested decision points and the start of the &lt;a href="http://www.aspiringcraftsman.com/tag/arrow-anti-pattern/"&gt;arrow anti-pattern&lt;/a&gt;) when trying to unpick the return values, especially trying to decode the possible return values when requesting that the far end server start up.&lt;/p&gt;
&lt;p&gt;Some of the repeated noise has been factored out into the &lt;code&gt;Atom&lt;/code&gt; active pattern for dealing with &lt;code&gt;{Reason, Payload}&lt;/code&gt;, but the underlying data model in OTP.Net is simply not very F# friendly.&lt;p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-1892598638117563688?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/1892598638117563688/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=1892598638117563688' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/1892598638117563688'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/1892598638117563688'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2010/12/otp-from-f.html' title='&amp;#8220;Hello, OTP!&amp;#8221; from F#'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-2979345161675751510</id><published>2010-12-28T11:00:00.004Z</published><updated>2010-12-28T11:46:37.311Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='anime'/><title type='text'>Anime  —  Giant Killing</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_Y0-w-d5dpBA/TRnDfjNbodI/AAAAAAAAA_0/irHJt96yqpM/s1600/GiantGillingBlog.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 270px;" src="http://1.bp.blogspot.com/_Y0-w-d5dpBA/TRnDfjNbodI/AAAAAAAAA_0/irHJt96yqpM/s320/GiantGillingBlog.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5555686562133156306" /&gt;&lt;/a&gt;
&lt;p&gt;When this title was announced for the spring season, my reaction was "Oh, football..." and pass it by -- but towards the end of its airing, it had become one of those series that hardly ever got talked about, but always positively when it was mentioned.  So, with a fairly thin line-up for autumn, I went back and gave the first episode a try...&lt;/p&gt;
&lt;p&gt;And it has turned out to be my pick for the best series of the year, by being so much not what the bulk of anime has become in recent years : the characters are adults, there's no romance, or borderline smut, or cute girls doing things cutely.  It also confirms the trend that series where the characters have perceptible noses are ones worth investigating.&lt;/p&gt;
&lt;p&gt;The story opens when East Tokyo United, a team in a prolonged slump, bring back a former star player, Takeshi Tatsumi, from England, where he as been coaching amateur (or at least not full-time professional) sides to good effect -- like taking them to the 4th round of the FA Cup.  This return as coach after having abandoned the team years before rankles amongst some of the players and many of the fans; the more so when he finally shows up at training, and chooses a starting side mainly from the reserves.&lt;/p&gt;
&lt;p&gt;Slowly, with a mixture of shrewd insight, and complete irreverence to the way things are done on and off the pitch (like sloping out of a press conference for an informal chat with the manager of the national team), Tatsumi starts to bring the team together. At last ETU notches up a few wins, and then in the climax of the series, have to face the Osaka Gunners, who in their previous match steamrollered a stronger side than ETU 8-0, in a match that actually spreads over more than 90 minutes of air time.&lt;/p&gt;
&lt;p&gt;The manga continues; we have only come half-way through the new season, and there isn't a plot-wrapped-up end.  If there were to be a second season, I'd watch it.&lt;/p&gt;
&lt;div class="center"&gt;&lt;object width="640" height="385"&gt;&lt;param name="movie" value="http://www.youtube.com/v/N1W9O0ZJmmc?fs=1&amp;amp;hl=en_GB"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/N1W9O0ZJmmc?fs=1&amp;amp;hl=en_GB" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="640" height="385"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-2979345161675751510?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/2979345161675751510/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=2979345161675751510' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/2979345161675751510'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/2979345161675751510'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2010/12/anime-giant-killing.html' title='Anime  &amp;#8212;  &lt;i&gt;Giant Killing&lt;/i&gt;'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_Y0-w-d5dpBA/TRnDfjNbodI/AAAAAAAAA_0/irHJt96yqpM/s72-c/GiantGillingBlog.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-3906478225629498294</id><published>2010-12-25T15:17:00.002Z</published><updated>2010-12-25T15:58:37.518Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='software practice'/><category scheme='http://www.blogger.com/atom/ns#' term='build'/><category scheme='http://www.blogger.com/atom/ns#' term='Installers'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Links for 25-Dec Bumper catch-up edition</title><content type='html'>&lt;p&gt;2010 having spanned the run from first integration point through to RTM and GA for &lt;a href="http://www.citrix.com/English/ps2/products/subfeature.asp?contentID=2300411"&gt;XenDesktop 5&lt;/a&gt;, I was being increasingly preoccupied as the year went on; and blogging of any form was one of the main casualties.  Which means I have a not-so-little list of backlog of things I want to keep track of from the last couple of months:&lt;/p&gt;
&lt;h4&gt;F#&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://tirania.org/blog/archive/2010/Nov-11.html"&gt;F# Goes Open Source&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://blogs.msdn.com/b/dsyme/archive/2010/12/22/monodevelop-plugin-for-f-now-available.aspx"&gt;MonoDevelop plugin for F# now available&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=" http://strangelights.com/blog/archive/2010/11/21/the-mvvm-pattern-using-f.aspx"&gt;The MVVM Pattern Using F#&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h4&gt;.net General&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://techmikael.blogspot.com/2010/11/creating-zip-files-with.html"&gt;Creating Zip files with System.IO.Packaging namespace&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.jroller.com/DhavalDalal/entry/kill_that_util_class"&gt;Kill That Util Class!&lt;/a&gt; with C# extension methods&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.abhisheksur.com/2010/11/debugging-with-async.html"&gt;Debugging with Async
&lt;/a&gt; -- C#5 CTP&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.simple-talk.com/dotnet/.net-framework/dynamic-language-integration-in-a-c-world/"&gt;Dynamic Language Integration in a C# World&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://weblogs.asp.net/fbouma/archive/2010/12/08/algorithmia-source-code-released-on-codeplex.aspx"&gt;Algorithmia Source Code released on CodePlex&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://tirania.org/blog/archive/2010/Dec-09-1.html"&gt;Mono: What we are Cooking&lt;/a&gt; (9-Dec-10)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://blogs.msdn.com/b/deva/archive/2010/12/10/how-to-determine-which-microsoft-net-framework-version-and-service-pack-installed.aspx"&gt;How to: Determine which Microsoft .Net Framework version and service pack installed?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://naveensrinivasan.com/2010/11/16/clr20r3/"&gt;Decoding clr20r3 .NET exception – using mono cecil&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://mikehadlow.blogspot.com/2010/12/c-closure-constructors.html"&gt;C# Closure Constructors&lt;/a&gt; -- from the &lt;a href="#jsclosure"&gt;original Javascript&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;Build Process&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Fixing &lt;a href="http://blogs.msdn.com/b/visualstudio/archive/2010/12/21/incorrect-solution-build-ordering-when-using-msbuild-exe.aspx"&gt;Incorrect solution build ordering when using MSBuild.exe&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://blogs.msdn.com/b/dohollan/archive/2010/11/11/stylecop-compliant-visual-studio-2010-code-snippets-november-release.aspx"&gt;StyleCop Compliant Visual Studio 2010 Code Snippets November Release&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.simple-talk.com/dotnet/.net-tools/taming-sandcastle-a-.net-programmers-guide-to-documenting-your-code/"&gt;Taming Sandcastle: A .NET Programmer's Guide to Documenting Your Code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://keithelder.net/blog/archive/2010/11/02/ilmerge-ndash-how-to-merge-assemblies-after-a-build.aspx"&gt;ILMerge – How to Merge Assemblies After A Build&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://blogs.technet.com/b/sateesh-arveti/archive/2010/11/21/msi-explorer.aspx"&gt;MSI Explorer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://coffeedrivendev.blogspot.com/2010/12/creating-nuget-package.html"&gt;Creating a NuGet Package&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;Misc.&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.codeproject.com/KB/IP/ThinVnc2.aspx"&gt;An HTML5 Remote Desktop - Part II&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://blogs.msdn.com/b/nickmalik/archive/2010/11/20/are-we-documenting-the-wrong-things.aspx"&gt;Are we documenting the wrong things?&lt;/a&gt;&lt;/li&gt;
&lt;li id="jsclosure"&gt;&lt;a href="http://mikehadlow.blogspot.com/2010/12/javascript-defining-classes-with.html"&gt;Javascript: Defining Classes with Closures&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-3906478225629498294?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/3906478225629498294/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=3906478225629498294' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/3906478225629498294'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/3906478225629498294'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2010/12/links-for-25-dec-bumper-catch-up.html' title='Links for 25-Dec Bumper catch-up edition'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-4887653270641539403</id><published>2010-12-25T12:09:00.007Z</published><updated>2010-12-25T16:24:31.723Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><title type='text'>F# GUI plumbing with reactive Events</title><content type='html'>&lt;p&gt;My most recent "Aha!" moment with the language is to finally wrap my head around the standard &lt;code&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/ee340422.aspx"&gt;Event&lt;/a&gt;&lt;/code&gt; module -- a sort of lightweight subset of the &lt;a href="http://msdn.microsoft.com/en-us/devlabs/ee794896"&gt;Reactive Extensions for .net&lt;/a&gt; -- while starting to do some more GUI programming in F#, which is making belated use of the &lt;a href="http://stevegilham.blogspot.com/2009/11/gtk-from-latest-ironpython-and-f.html"&gt;GTK#&lt;/a&gt;/&lt;a href="http://stevegilham.blogspot.com/2010/01/glade-from-f.html"&gt;Glade&lt;/a&gt; spikes I made about a year ago.&lt;/p&gt;
&lt;p&gt;It may not make much difference in the simplest cases where&lt;/p&gt;
&lt;pre class="brush: c#"&gt;ok_btn.Clicked += new EventHandler(OnButtonClick);&lt;/pre&gt;
&lt;p&gt;naively went to&lt;/p&gt;
&lt;pre class="brush: f#"&gt;ok_btn.Clicked.AddHandler(fun source arguments -&amp;gt; OnButtonClick source arguments)&lt;/pre&gt;
&lt;p&gt;but which is equivalent to, using the &lt;code&gt;Event&lt;/code&gt; module,&lt;/p&gt;
&lt;pre class="brush: f#"&gt;ok_btn.Clicked
   |&amp;gt; Event.add (OnButtonClick ok_btn)&lt;/pre&gt;
&lt;p&gt;At this point the main difference is the change in the function signature -- first, it is a real F# function rather than being forced to use the implicit cast from a fun to a delegate, even if the function signatures look compatible on the surface; second the function passed to &lt;code&gt;Event.add&lt;/code&gt; doesn't have the source argument (but, as shown, that can be brought in as a closure).&lt;/p&gt;

&lt;p&gt;So that's a small win from the beginning -- but that just scratches the surface.  Where it actually shines is where multiple sources of events have to be handled in much the same way.  For example, given a menu containing recently accessed files, with selection meaning to re-open that file, as if they had been selected from a file open action&lt;/p&gt;
&lt;pre class="brush: f#"&gt;
   // Do something only if we actually pick a file
   // Use the built-in identity function as a 'T -&amp;gt; 'U option mapper here
   // for the case 'T being a FileInfo option and 'U being FileInfo
   let click = handler.openButton.Clicked
                 |&amp;gt; Event.map (fun a -&amp;gt; (* 
                           open a file dialog, 
                           get a FileInfo option from the selected file name; 
                           where cancel =&amp;gt; None *))
                 |&amp;gt; Event.choose id

   // Selecting an item from the menu                                       
   let select =    
       handler.openMenu.AllChildren
       |&amp;gt; Seq.cast&amp;lt;MenuItem&amp;gt;
       |&amp;gt; Seq.map (fun (i:MenuItem) -&amp;gt; i.Activated
                                       |&amp;gt; Event.map (fun a -&amp;gt; new FileInfo((i.Child :?&amp;gt; Label).Text))
                                       |&amp;gt; Event.filter (fun (i:FileInfo) -&amp;gt; i.Exists))

   // The sum of all these events                                  
   let accumulation = select
                      |&amp;gt; Seq.fold Event.merge click  

   // do things with a file selection event                   
   accumulation |&amp;gt; Event.add UpdateRecentFileList
   accumulation |&amp;gt; Event.add LoadFile
   ...
&lt;/pre&gt;
&lt;p&gt;All the inhomogeneity of the event sources (button click, menu item activation) and their associated &lt;code&gt;EventArgs&lt;/code&gt; types is smoothed away by appropriate &lt;code&gt;Event.map&lt;/code&gt; calls and the use of closures; different validations are handled by appropriate &lt;code&gt;Event.filter&lt;/code&gt; or &lt;code&gt;Event.choose&lt;/code&gt; invocations; and then the whole ensemble is gathered together by an appropriate fold with &lt;code&gt;Event.merge&lt;/code&gt; as the accumulator.&lt;/p&gt;
&lt;p&gt;True, there is nothing here that could not be achieved by making old-style handler functions for &lt;code&gt;AddHandler&lt;/code&gt; from equivalent appropriately composed function chains -- the path I was starting to take for this bit of plumbing before I was indirectly reminded of this feature, and dug up &lt;a href="http://tomasp.net/blog/reactive-i-fsevents.aspx"&gt;this series of posts&lt;/a&gt; from a couple of years back (so slightly dated in the details).  The big difference that using the &lt;code&gt;Event&lt;/code&gt; module makes is that it is more expressive of intent, and separates the concerns -- general event handling plumbing vs this application's business logic -- explicitly.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-4887653270641539403?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/4887653270641539403/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=4887653270641539403' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/4887653270641539403'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/4887653270641539403'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2010/12/f-gui-plumbing-with-reactive-events.html' title='F# GUI plumbing with reactive Events'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-5085909028309455910</id><published>2010-12-25T11:41:00.003Z</published><updated>2010-12-25T12:02:27.987Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='winter'/><category scheme='http://www.blogger.com/atom/ns#' term='weather'/><category scheme='http://www.blogger.com/atom/ns#' term='cycling'/><title type='text'>A white-ish Christmas</title><content type='html'>&lt;p&gt;Last Saturday started dry but cold, though with some ice still on the road outside -- cycling into town in multiple layers, with only my nose exposed, that was very cold for the first couple of miles until I started to radiate enough heat through it.  I got back home mid-afternoon, and took a nap, waking to find the world turned white.&lt;/p&gt;
&lt;p&gt;So it was a work-from-home start of the week, while waiting for the ungritted roads in the village to be pounded clear; with a brisk yomp to Waitrose to get me out of the house.  By Thursday, as the threatened midweek snow had come to nothing, I did go into the office (probably getting a lot less done than had I stayed at home).&lt;/p&gt;
&lt;p&gt;Yesterday, I did the approximately biennial feat of cycling into work for the shortest working day -- although the fields were white, and the sky grey, the road was dry, and the weather, although still below zero on the way in, was milder than it had been (showing quite how accustomed we soon become to the cold).  And in doing so that put me over the 1000 miles for this half year.&lt;/p&gt;
&lt;p&gt;Today is brighter and sunny, but colder.  Where I cleared the drive is dry, but the untouched snow is showing no signs of turning to slush like it had a few days ago.  I shall have to make my way down the garden to check the greenhouse and the broccoli.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-5085909028309455910?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/5085909028309455910/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=5085909028309455910' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/5085909028309455910'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/5085909028309455910'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2010/12/white-ish-christmas.html' title='A white-ish Christmas'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-1502308334351600291</id><published>2010-12-07T09:35:00.001Z</published><updated>2010-12-07T09:37:00.222Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='winter'/><category scheme='http://www.blogger.com/atom/ns#' term='weather'/><category scheme='http://www.blogger.com/atom/ns#' term='don&apos;t talk to me about life'/><title type='text'>Winter Wonderland</title><content type='html'>&lt;p&gt;The car thermometer showed -6 as I started out to work at about 08:45 this morning, dropping quickly to -8, and then slowly recovering to -7.  Isn't it time we had an apology for &lt;a href="http://www.independent.co.uk/environment/snowfalls-are-now-just-a-thing-of-the-past-724017.html"&gt;predictions like this one&lt;/a&gt; from ten years ago?&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-1502308334351600291?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/1502308334351600291/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=1502308334351600291' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/1502308334351600291'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/1502308334351600291'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2010/12/winter-wonderland.html' title='Winter Wonderland'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-6581506559045296516</id><published>2010-11-22T08:12:00.004Z</published><updated>2010-11-22T08:30:33.147Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='lol people'/><category scheme='http://www.blogger.com/atom/ns#' term='food'/><title type='text'>Sometimes, a man's gotta do what a man's gotta do...</title><content type='html'>&lt;p&gt;I encountered &lt;a href="http://coldfury.com/?p=26859"&gt;this outburst of testosterone fuelled experimentation&lt;/a&gt; a couple of weeks ago, and mentioned it to Karen -- who immediately said that this was something that had to be done.  So, yesterday I actually made some of the &lt;blockquote&gt;&lt;p&gt;Manliest Bread EVARR!!!!!111elebenty!!!!!!1 &lt;sup&gt;&lt;a href="#phyto221110"&gt;*&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Bacon. Jalapeno. Cheesy. Beer. Bread. The sheer awesomeness of the concept forced that punctuation, I swear.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;And it was a great success.&lt;/p&gt;

&lt;p&gt;The recipe (for a bread making machine)&lt;/p&gt;

&lt;p&gt;Bread chassis:&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;1 tsp yeast&lt;/li&gt;
&lt;li&gt;1.25 tsp salt&lt;/li&gt;
&lt;li&gt;1.5 tsp sugar&lt;/li&gt;
&lt;li&gt;350g strong white flour&lt;/li&gt;
&lt;li&gt;150g strong wholemeal flour&lt;/li&gt;&lt;/ul&gt;

&lt;p&gt;And in the main mix&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;85g of &lt;a href="http://www.foodepedia.co.uk/food/2009/dec/cornish_quartz.htm"&gt;the strongest cheddar I had in the house&lt;/a&gt;, grated&lt;/li&gt;
&lt;li&gt;320ml ale -- I used Greene King IPA&lt;/li&gt;&lt;/ul&gt;

&lt;p&gt;In the candied fruit hopper:&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;4 rashers of thick-cut bacon, dry-fried (pouring off any oozing water as required) until stiff, rinds removed and chopped into ~1cm bits&lt;/li&gt;
&lt;li&gt;As many &lt;a href="http://www.mysupermarket.co.uk/ocado-compare-prices/Pickles_And_Marinated_Vegetables/Waitrose_Cooks_Ingredients_Jalapeno_Peppers_220g.html"&gt;jalapeno pepper slices&lt;/a&gt; as will fit.&lt;/li&gt;&lt;/ul&gt;

&lt;p&gt;Cook on standard raisin bread program.  Cool for an hour, then enjoy!&lt;/p&gt;

&lt;p&gt;It was wonderfully savoury warm with butter, and made a fine accompaniment to a chilli powered by the fruits of our greenhouse (to the extent of 2/3 of the loaf).  Though I couldn't help feeling that there had to be some way to work some garlic into the mix without spoiling the awesome.&lt;/p&gt;

&lt;p&gt;&lt;sup&gt;&lt;a id="phyto221110" name="phyto221110"&gt;*&lt;/a&gt;&lt;/sup&gt; OK, some pedants will point out that this recipe is just loaded with phytoestrogens.  But it's the concept that counts.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-6581506559045296516?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/6581506559045296516/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=6581506559045296516' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/6581506559045296516'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/6581506559045296516'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2010/11/sometimes-mans-gotts-do-what-mans-gotta.html' title='Sometimes, a man&apos;s gotta do what a man&apos;s gotta do...'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-5950487293336714571</id><published>2010-11-14T10:14:00.003Z</published><updated>2010-11-14T10:29:23.767Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='anime'/><title type='text'>Anime — Strike Witches 2</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_Y0-w-d5dpBA/TN-27KqfxcI/AAAAAAAAA_c/jR17__B6-3Q/s1600/sw2.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 288px; height: 320px;" src="http://1.bp.blogspot.com/_Y0-w-d5dpBA/TN-27KqfxcI/AAAAAAAAA_c/jR17__B6-3Q/s320/sw2.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5539347194280134082" /&gt;&lt;/a&gt;
&lt;p&gt;As hinted by the ending of &lt;a href="http://stevegilham.blogspot.com/2008/10/anime-strike-witches.html"&gt;the previous series&lt;/a&gt;, further adventures of half-dressed alternate- WWII aces; despite not being quite popular enough to stop the original studio going under, the franchise was considered worth taking up.&lt;/p&gt;
&lt;p&gt;Unfortunately, much of the charm of the first series -- the copious period references, and character driven episodes -- was lost in favour of even more bath scenes and tacky comedy.  When, finally, an attempt at plot and drama wobbled into sight in the last couple of episodes, it didn't have any foundation to rest upon.&lt;/p&gt;
&lt;p&gt;Overall, rather disappointing for being pretty much what the premise would lead you to expect, and no more.  Will I watch the inevitable third season?  Probably.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-5950487293336714571?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/5950487293336714571/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=5950487293336714571' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/5950487293336714571'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/5950487293336714571'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2010/11/anime-strike-witches-2.html' title='Anime &amp;#8212; Strike Witches 2'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_Y0-w-d5dpBA/TN-27KqfxcI/AAAAAAAAA_c/jR17__B6-3Q/s72-c/sw2.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-2887023371754619669</id><published>2010-11-08T18:06:00.001Z</published><updated>2010-11-08T18:07:53.139Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='silverlight'/><category scheme='http://www.blogger.com/atom/ns#' term='Unit testing'/><category scheme='http://www.blogger.com/atom/ns#' term='Computer Security'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><category scheme='http://www.blogger.com/atom/ns#' term='concurrency'/><title type='text'>Links for 8-Nov</title><content type='html'>&lt;p&gt;Asynchronous programming in .net&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://tomasp.net/blog/csharp-fsharp-async-intro.aspx"&gt;Async programming&lt;/a&gt; in F#2 and the PDC announced C#5 compared.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://blogs.msdn.com/b/dsyme/archive/2010/10/21/the-f-asynchronous-programming-model-padl-2010-pre-publication-draft.aspx"&gt;The F# model&lt;/a&gt; in detail.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://weblogs.asp.net/shijuvarghese/archive/2010/10/29/visual-studio-async-ctp.aspx"&gt;CTP downloads&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Jon Skeet plays around with the internals of the CTP release &lt;a 
href="http://msmvps.com/blogs/jon_skeet/archive/2010/10/30/c-5-async-investigating-control-flow.aspx"&gt;here&lt;/a&gt; and &lt;a 
href="http://msmvps.com/blogs/jon_skeet/archive/2010/10/31/c-5-async-experimenting-with-member-resolution-getawaiter-beginawait-endawait.aspx"&gt;here&lt;/a&gt;. &lt;/li&gt;
&lt;li&gt;Related : &lt;a href="http://blogs.msdn.com/b/ericlippert/archive/2010/10/27/continuation-passing-style-revisited-part-five-cps-and-asynchrony.aspx"&gt;continuation passing style and asynchrony&lt;/a&gt; i.e. what the compiler is doing for you with &lt;code&gt;await&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Also from the PDC -- &lt;a href="http://davybrion.com/blog/2010/10/do-you-still-believe-in-silverlight/"&gt;what next for Silverlight&lt;/a&gt;?&lt;/p&gt;
&lt;p&gt;&lt;a href="http://blogs.msdn.com/b/rxteam/archive/2010/10/28/rx-design-guidelines.aspx"&gt;Rx Design Guidelines&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://oakleafblog.blogspot.com/2010/10/adam-langley-points-out-that-ssltls.html"&gt;SSL/TLS isn't computationally expensive&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://nuget.codeplex.com/"&gt;NuGet&lt;/a&gt; -- gems for .net has a new name.&lt;br&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="http://should.codeplex.com/"&gt;Should &lt;/a&gt;Assertion Library.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://blogs.msdn.com/b/ramg/archive/2010/10/09/integrating-code-documentation-into-build-process.aspx"&gt;Sandcastle MSBuild integration&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/5569894-2887023371754619669?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/2887023371754619669/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=2887023371754619669' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/2887023371754619669'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/2887023371754619669'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2010/11/links-for-8-nov.html' title='Links for 8-Nov'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-2428350970315190821</id><published>2010-10-29T18:54:00.004+01:00</published><updated>2010-10-29T19:20:15.327+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='anime'/><title type='text'>Anime  —  The Book of Bantorra : Armed Librarians</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_Y0-w-d5dpBA/TMsKc8EeFzI/AAAAAAAAA_Q/C_Prs_kpzn0/s1600/bantorra-r.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 270px;" src="http://3.bp.blogspot.com/_Y0-w-d5dpBA/TMsKc8EeFzI/AAAAAAAAA_Q/C_Prs_kpzn0/s320/bantorra-r.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5533528059432277810" /&gt;&lt;/a&gt;
&lt;p&gt;A belated review, by some months, belated enough that it's fallen off the Crunchryroll roster.&lt;/p&gt;
&lt;p&gt;I tried the first episode late last year, and wasn't impressed, but went back after a lot of talk about how the series was getting awesome later on; and fortunately the awful CGI from the first episode seemed to be a one-off.&lt;/p&gt;
&lt;p&gt;In a climate where cute girls doing cute things and high-school romance seemed to have driven almost everything else off the table, Bantorra was a welcome return to an adult cast (with well endowed women) and OTT violence.  However shoehorning a series of ten light novels with more plot twists than you could shake a stick at into a 26 + 1 recap episode series didn't quite work out so well in the end.&lt;/p&gt;
&lt;p&gt;The setting was interesting and unusual in many ways -- a sort of alternate 1940s, where magic also exists, and people have made up European-style names that look and sound more like keyboard mashing (Olivia Litlets, Parney Pealrmanta, Enrique Bis'hile...); in a world where at death, a memory snapshot fossilizes as a "book" that can be "read" by touching it.&lt;/p&gt;
&lt;p&gt;So the big struggle starts off between the Armed Librarians under Acting Director Hamyuts Meseta of the Bantorra Library, and the Church of Drowning in God's Grace, whose noble thoughts about the value of humans is belied by their separating their flock into True Men (at the top), Mock Men (mid-range operatives) and Meats (the masses). And then things get more and more complicated, with -- except in the occasional digressive arc for a bit of back-story -- the villains of the piece changing again and again, and major characters being killed all over the show.&lt;/p&gt;
&lt;p&gt;Perhaps had it been a 40-episode series it might not have seemed quite so erratic and all over the place; or maybe it would have still seemed like it was trying to fit in everything and the kitchen sink.  Overall a decent enough piece of entertainment and one that at least did all it could to be different from the current pack.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-2428350970315190821?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/2428350970315190821/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=2428350970315190821' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/2428350970315190821'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/2428350970315190821'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2010/10/anime-book-of-bantorra-armed-librarians.html' title='Anime  &amp;#8212;  &lt;i&gt;The Book of Bantorra : Armed Librarians&lt;/i&gt;'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_Y0-w-d5dpBA/TMsKc8EeFzI/AAAAAAAAA_Q/C_Prs_kpzn0/s72-c/bantorra-r.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-8133705763284240430</id><published>2010-10-27T19:06:00.004+01:00</published><updated>2010-10-27T19:50:13.326+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='weather'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Javascript Airport weather decoder</title><content type='html'>&lt;p&gt;While &lt;a href="http://www.weatherpixie.com/"&gt;weatherpixie&lt;/a&gt; remains on extended hiatus, I have finally put together a simple in-browser replacement.  It uses James Padolsey's &lt;a href="http://james.padolsey.com/javascript/cross-domain-requests-with-jquery/"&gt;cross-domain Ajax plug-in&lt;/a&gt; for jQuery (indirecting via YQL), a &lt;a href="http://heras-gilsanz.com/manuel/METAR-Decoder.html"&gt;simple parsing script from Manuel Heras&lt;/a&gt;, here separated into a stand-alone script, and an &lt;code&gt;iframe&lt;/code&gt;-based page to do the work.&lt;/p&gt;
&lt;p&gt;The final piece of heavy lifting is to get the appropriate METAR report for the local airfield, and that's just a few lines of javascript --&lt;/p&gt;
&lt;pre class="brush: jscript"&gt;  $(document).ready(function(){
    var lmurl = "http://weather.noaa.gov/mgetmetar.php?cccc=EGSC&amp;amp;Submit=SUBMIT"
    $.get(lmurl, function(res) { 
          $(res.responseText).find('hr + p').each(function(i, a) {
            var text = a.innerText;
            if (text == undefined) text = a.textContent;
            text = do_metar(text);
            while (text.indexOf('\n') &amp;gt;= 0)
              text = text.replace('\n', '&amp;lt; br /&amp;gt;');
            $('#report').html(text);
            });
          });
    });&lt;/pre&gt;
&lt;p&gt;Here, EGSC is the &lt;a href="http://www.mapping.com/airportcodes.html"&gt;ICAO code&lt;/a&gt; for Cambridge Airport.  And that's a lot closer to home than the Met. Office who give out &lt;a href="http://www.metoffice.gov.uk/weather/uk/ee/cambridge_forecast_weather.html"&gt;current conditions at Bedford&lt;/a&gt; on the Cambridge forecast!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-8133705763284240430?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/8133705763284240430/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=8133705763284240430' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/8133705763284240430'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/8133705763284240430'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2010/10/javascript-airport-weather-decoder.html' title='Javascript Airport weather decoder'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-9151078230454307816</id><published>2010-10-12T18:30:00.000+01:00</published><updated>2010-10-12T18:31:09.621+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lol people'/><title type='text'>This made me laugh</title><content type='html'>&lt;p&gt;Though by rights, it shouldn't -- &lt;a href="http://www.telegraph.co.uk/news/8048844/Brits-sense-of-humour-fails-at-the-age-of-52-study-finds.html"&gt;http://www.telegraph.co.uk/news/8048844/Brits-sense-of-humour-fails-at-the-age-of-52-study-finds.html&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/5569894-9151078230454307816?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/9151078230454307816/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=9151078230454307816' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/9151078230454307816'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/9151078230454307816'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2010/10/this-made-me-laugh.html' title='This made me laugh'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-2649979732861285977</id><published>2010-10-09T15:26:00.005+01:00</published><updated>2010-10-09T15:35:15.826+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='cycling'/><category scheme='http://www.blogger.com/atom/ns#' term='don&apos;t talk to me about life'/><title type='text'>Road rage</title><content type='html'>&lt;p&gt;Some weeks ago, I was working late, and so cycling home after the usually flurry of commuter traffic.&lt;/p&gt;
&lt;p&gt;Coasting one of the downhill parts, doing in the 15-20mph range, I suddenly heard this strange whirring sound behind me -- and then, suddenly, a whole flotilla of sport cyclists.&lt;/p&gt;
&lt;p&gt;Now, when they travel solo, going along in their spray-on advertising, nose on the tarmac, arse in conjunction with the planet Jupiter, they are just subjects of merry jest.  When they travel in packs, it's different.&lt;/p&gt;
&lt;p&gt;Six to eight pairs of them streamed past me, peddling like the clappers, leaving little to no clearance, doing only the absolute minimum to move out of their straight line and pass, nearly forcing me into the verge -- much more threatening and aggressive road use than any of the buses or farm vehicles I usually have to contend with.&lt;/p&gt;
&lt;p&gt;Between their silly outfits and bad behaviour, the sort of thing that gives  cyclists a bad name, and discourages cycling as a more relaxed form of transport.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-2649979732861285977?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/2649979732861285977/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=2649979732861285977' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/2649979732861285977'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/2649979732861285977'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2010/10/road-rage.html' title='Road rage'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-1583601706749997929</id><published>2010-10-07T19:22:00.002+01:00</published><updated>2010-10-09T15:25:57.340+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Mileage'/><category scheme='http://www.blogger.com/atom/ns#' term='cycling'/><title type='text'>A season in the saddle</title><content type='html'>&lt;p&gt;Three months ago, I finally got around to getting a cheap and cheerful trip computer for my bike.  Three months later I have done 767.4 miles at an average of 11 mph, as well as measuring that the final part of my journey to work that I couldn't measure with the car odo, starting with an off-road part, comes to just over 1.6 miles each way.&lt;/p&gt;
&lt;p&gt;This also measures up to my order of magnitude guesswork that I average closer to 2000 miles in the saddle each year than 1000.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-1583601706749997929?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/1583601706749997929/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=1583601706749997929' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/1583601706749997929'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/1583601706749997929'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2010/10/season-in-saddle.html' title='A season in the saddle'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-9089118694722092939</id><published>2010-10-05T20:54:00.000+01:00</published><updated>2010-10-05T20:54:56.713+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='Computer Security'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Computing per-service SIDs without sc.exe</title><content type='html'>&lt;p&gt;How it's done is well known:&lt;/p&gt;

&lt;blockquote&gt;&lt;a href="http://blogs.technet.com/b/voy/archive/2007/03/22/per-service-sid.aspx"&gt;When configured to have a per-service SID (i.e. type of SID either "Unrestricted" or "Restricted"), the service SID is computed as S-1-5-80-{SHA-1(service name in uppercase)}&lt;/a&gt;&lt;/blockquote&gt;

&lt;p&gt;Here's a quick script to do it, that can be the basis for including the computation programmatically into e.g. installer generation.&lt;/p&gt;

&lt;pre class="brush: f#"&gt;open System.Security.Cryptography
open System.Security.Principal
open System.Text
open System

// 1st arg (index [0]) is this script
let name = fsi.CommandLineArgs.[1]
let hash = new SHA1CryptoServiceProvider()
let tail = name.ToUpper()
              |&gt; Encoding.Unicode.GetBytes
              |&gt; (fun x -&gt; hash.ComputeHash(x))
              
let head = [|1uy; 6uy; 0uy; 0uy; 0uy; 0uy; 0uy; 5uy; 80uy; 0uy; 0uy; 0uy |]
let binary = Array.append head tail

let sid = new SecurityIdentifier(binary, 0)
Console.WriteLine(sid.ToString())&lt;/pre&gt;

&lt;p&gt;So running it we get:&lt;/p&gt;

&lt;pre class="code"&gt;&amp;gt;&amp; 'C:\Program Files\FSharp-2.0.0.0\bin\fsi.exe' .\ssid.fsx MyService
S-1-5-80-517257762-1253276234-605902578-3995580692-1133959824
&amp;gt;&lt;/pre&gt;

&lt;p&gt;which compares nicely with:&lt;/p&gt;

&lt;pre class="code"&gt;&amp;gt;sc showsid MyService

NAME: MyService
SERVICE SID: S-1-5-80-517257762-1253276234-605902578-3995580692-1133959824
&amp;gt;&lt;/pre&gt;

&lt;p&gt;where each of the 5 trailing facets is just the decimal representation of 4 bytes of the SHA-1 hash taken as a little-endian unsigned integer.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-9089118694722092939?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/9089118694722092939/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=9089118694722092939' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/9089118694722092939'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/9089118694722092939'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2010/10/computing-per-service-sids-without.html' title='Computing per-service SIDs without sc.exe'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-6975383750839118771</id><published>2010-09-25T01:55:00.001+01:00</published><updated>2010-09-25T01:57:26.976+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Film'/><title type='text'>Film — True Legend (Su Qi-Er)</title><content type='html'>&lt;p&gt;Woo-Ping Yuen's latest chop-socky film about Beggar Su, the master of Dunken Fist wu-shu had its UK première here this evening.&lt;/p&gt;
&lt;p&gt;It was almost solid martial arts mayhem, with just a few pauses for breath; however the structure of the film was a bit of a mess -- the intrusive 'now put on your 3D glasses/OK take them off again' around the training arc and what looked like it was going to be the Boss Fight didn't help (nor did the 3D itself where the actors often seemed to be floating detached from the rather washed-out scene around them.&lt;/p&gt;
&lt;p&gt;The story itself seems to be a fairly direct one about Su and his adopted brother Yuan who spurns the generosity of his adopted family, and cue two-way revenge tragedy -- then just when you think it's all over, a long coda culminating in the real Boss Fight out of nowhere.&lt;/p&gt;
&lt;p&gt;Clearly it must be following the high points of a well known tale -- but it ends up feeling unresolved because of the broken narrative rhythm, whereas stopping at the tragedy of the 3/4 mark would have given closure (if you don't ask too deeply why a martial artist of Su's calibre would dig a box out of sandy ground with his bare hands, rather than using some one-inch-punch technique to break open the lid &lt;span style="font-style:italic;"&gt;in situ&lt;/span&gt;).&lt;/p&gt;
&lt;p&gt;In all, not entirely satisfying popcorn fare.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-6975383750839118771?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/6975383750839118771/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=6975383750839118771' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/6975383750839118771'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/6975383750839118771'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2010/09/film-true-legend-su-qi-er.html' title='Film &amp;#8212; &lt;i&gt;True Legend (Su Qi-Er)&lt;/i&gt;'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5569894.post-7171439155323092802</id><published>2010-09-22T10:19:00.002+01:00</published><updated>2010-09-22T10:24:36.519+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Film'/><title type='text'>Film — Henri Quatre (Henry of Navarre)</title><content type='html'>&lt;p&gt;Added to the Film Festival programme after the program went to print, a competently done edited-highlights costume drama (German production, dialogue in French with some Italian and Latin) of the life and turbulent times of the eponymous French King.&lt;/p&gt;
&lt;p&gt;Not something I would have gone out of my way to see, had I not already earmarked this week for the festival, but equally not something where I felt I'd wasted the afternoon.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5569894-7171439155323092802?l=stevegilham.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stevegilham.blogspot.com/feeds/7171439155323092802/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5569894&amp;postID=7171439155323092802' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/7171439155323092802'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5569894/posts/default/7171439155323092802'/><link rel='alternate' type='text/html' href='http://stevegilham.blogspot.com/2010/09/film-henri-quatre-henry-of-navarre.html' title='Film &amp;#8212; &lt;i&gt;Henri Quatre (Henry of Navarre)&lt;/i&gt;'/><author><name>Steve</name><uri>http://www.blogger.com/profile/03622573187942388226</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://photos1.blogger.com/blogger/1204/204/320/Avvie_180.jpg'/></author><thr:total>0</thr:total></entry></feed>
