Tuesday, May 31, 2011

CodeDOM and poor man's lambdas -- Part 2

Unfortunately there is one major sticking point in getting the generation to work for F# -- local variables are declared as let mutable rather than as a ref type which means that the necessary closure cannot be made; this is rather stronger than the lack of support for CodeDefaultValueExpression, 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.

That aside, the main operation looks like this, similar to the previous examples

let GenerateWrapper (target:Type) =
// Create a compile unit with a namespace
let compileUnit = new CodeCompileUnit()
let scope = new CodeNamespace("Derived")
compileUnit.Namespaces.Add( scope ) |> ignore
// Create a GeneratedCodeAttribute expression
let generated = typeof<GeneratedCodeAttribute>
let ga = new CodeAttributeDeclaration(TypeRef generated)
ga.Arguments.Add(new CodeAttributeArgument( new CodePrimitiveExpression("Generator"))) |> ignore
ga.Arguments.Add(new CodeAttributeArgument( new CodePrimitiveExpression("0.0.0.0"))) |> ignore
// Create the wrapper type and attribute it
let wrapper = new CodeTypeDeclaration(target.Name + "Wrapper")
wrapper.TypeAttributes <- TypeAttributes.Public ||| TypeAttributes.Sealed
wrapper.CustomAttributes.Add(ga) |> ignore
scope.Types.Add(wrapper) |> ignore
// Add a field for the wrapped object
let this = new CodeMemberField(TypeRef target, "this")
wrapper.Members.Add(this) |> ignore
// Create a simple constructor to set the wrapped object
let construct = new CodeConstructor()
construct.Parameters.Add(new CodeParameterDeclarationExpression(TypeRef target, target.Name)) |> ignore
let self = new CodeThisReferenceExpression()
let atThis = new CodeFieldReferenceExpression(self, "this")
let assign = new CodeAssignStatement(atThis, new CodeArgumentReferenceExpression(target.Name))
construct.Statements.Add(assign) |> ignore
wrapper.Members.Add(construct) |> ignore
// Create a set to hold namespaces
let namespaces = new HashSet<string>()
// Get all the methods of interest (properties would be similar)
let methods = target.GetMethods(BindingFlags.Instance ||| BindingFlags.Public ||| BindingFlags.DeclaredOnly)
|> Seq.filter (fun x -> not x.IsSpecialName )
|> Seq.sortBy (fun p -> p.Name)
// Accumulate all referenced namespaces (aside from generic types)
methods
|> Seq.iter (fun x -> namespaces.Add(x.ReturnType.Namespace) |> ignore)
methods
|> Seq.collect (fun m -> m.GetParameters())
|> Seq.iter (fun x -> namespaces.Add(x.ParameterType.Namespace) |> ignore)
// Generate a proxy class for each call
methods
|> Seq.map (GenerateClass target ga)
|> Seq.map wrapper.Members.Add
|> Seq.iter ignore
// Generate a delegating method for each call
methods
|> Seq.map (GenerateProxy target)
|> Seq.map wrapper.Members.Add
|> Seq.iter ignore
// Add in all the namespaces
namespaces.Add(target.Namespace) |> ignore
namespaces.Add(generated.Namespace) |> ignore
namespaces
|> Seq.sort
|> Seq.iter (fun n-> scope.Imports.Add(new CodeNamespaceImport(n)))
compileUnit
view raw gistfile1.fs hosted with ❤ by GitHub

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.

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 @this or proxy 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:

let GenerateClass (target:Type) (ga:CodeAttributeDeclaration)(m:MethodInfo) =
// Generate the type and mark as generated
let newtype = new CodeTypeDeclaration(m.Name + "__Proxy")
newtype.TypeAttributes <- TypeAttributes.NestedPrivate /// .NotPublic ||| TypeAttributes.Sealed
newtype.CustomAttributes.Add(ga) |> ignore
// add a field to point to the wrapped object
let this = new CodeMemberField(TypeRef target, "this")
this.Attributes <- MemberAttributes.Public
newtype.Members.Add(this) |> ignore
// Fields to allow us to close over all the parameters
m.GetParameters()
|> Seq.map GenerateField
|> Seq.map newtype.Members.Add
|> Seq.iter ignore
// The lambda method
let proxy = new CodeMemberMethod()
proxy.Name <- m.Name
proxy.Attributes <- MemberAttributes.Public ||| MemberAttributes.Final
// Fudge the case of a void method
proxy.ReturnType <- TypeRef(m.ReturnType)
if m.ReturnType = typeof<System.Void> then proxy.ReturnType <- TypeRef(typeof<int>)
// 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()
|> Seq.map (fun x -> let ex = new CodeFieldReferenceExpression(self, x.Name)
let dir = if x.IsIn && x.IsOut then FieldDirection.Ref
else if x.IsOut then FieldDirection.Out
else FieldDirection.In
new CodeDirectionExpression(dir, ex) :> CodeExpression)
|> Seq.toArray
let invoke = new CodeMethodInvokeExpression(call, parameters)
// return a dummy zero instead of void, or the result
if m.ReturnType = typeof<System.Void> then
proxy.Statements.Add invoke |> ignore
let result = new CodeMethodReturnStatement(new CodePrimitiveExpression(0))
proxy.Statements.Add result |> ignore
else
proxy.Statements.Add (new CodeMethodReturnStatement(invoke)) |> ignore
// add this method to the class
newtype.Members.Add(proxy) |> ignore
newtype
view raw gistfile1.fs hosted with ❤ by GitHub

where the individual field declarations have to work around the decorations for out or ref, thus:

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 <- MemberAttributes.Public
field
view raw gistfile1.fs hosted with ❤ by GitHub

The generation of the proxy methods is equally mechanical

let GenerateProxy (target:Type) (m:MethodInfo) =
// Generate the method and its parameter list
let proxy = new CodeMemberMethod()
proxy.Name <- m.Name
proxy.Attributes <- MemberAttributes.Public ||| MemberAttributes.Final
proxy.ReturnType <- TypeRef(m.ReturnType)
m.GetParameters()
|> Seq.iter ( fun (t:ParameterInfo) ->
let px = new CodeParameterDeclarationExpression()
px.Direction <- if t.IsIn && t.IsOut then FieldDirection.Ref
else if t.IsOut then FieldDirection.Out
else FieldDirection.In
px.Name <- t.Name
let tt = if t.ParameterType.IsByRef then t.ParameterType.GetElementType() else t.ParameterType
px.Type <- TypeRef(tt)
proxy.Parameters.Add(px) |> 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 |> 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 |> ignore
// initialize all the other fields (to default if out only)
m.GetParameters()
|> Seq.map (fun x -> let left = new CodeFieldReferenceExpression(proxyRef, x.Name)
let right = if x.IsOut && (not x.IsIn) then
// Doesn't work in the F# CodeDOM
new CodeDefaultValueExpression(TypeRef (x.ParameterType.GetElementType())) :> CodeExpression
else
new CodeArgumentReferenceExpression(x.Name) :> CodeExpression
new CodeAssignStatement(left, right)
)
|> Seq.map proxy.Statements.Add
|> Seq.iter ignore
// Expect the lambda to return int for a void method
let typeRef = if m.ReturnType = typeof<System.Void> then TypeRef(typeof<int>)
else TypeRef(m.ReturnType)
// call the Wrapping method which takes the lambda
let funcType = CodeTypeReference("Func")
funcType.TypeArguments.Add typeRef |> 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 |> ignore
// copy back all the out or ref parameters
m.GetParameters()
|> Seq.filter (fun x -> x.IsOut)
|> Seq.map (fun x -> let left = new CodeArgumentReferenceExpression(x.Name)
let right = new CodeFieldReferenceExpression(proxyRef, x.Name)
new CodeAssignStatement(left, right)
)
|> Seq.map proxy.Statements.Add
|> Seq.iter ignore
// and for a non-void method, return the value from the lambda
if m.ReturnType <> typeof<System.Void> then
let r = new CodeMethodReturnStatement(CodeVariableReferenceExpression("return"))
proxy.Statements.Add r |> ignore
proxy
view raw gistfile1.fs hosted with ❤ by GitHub

So, given a simple type

public class Subject
{
public int DoIt(int argument, out string result) {...}
public void DoItSilently(int argument, out string result) {...}
}
view raw gistfile1.cs hosted with ❤ by GitHub

we generate

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<int>(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<int>(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;
}
}
}
}
view raw gistfile1.fs hosted with ❤ by GitHub

The F# code contains

let mutable (proxy:SubjectWrapper_DoIt__Proxy) = new SubjectWrapper_DoIt__Proxy()
...
proxy.result <- (* Unknown expression type 'CodeDefaultValueExpression' please report this to the F# team. *)
view raw gistfile1.fs hosted with ❤ by GitHub

Manually replacing this with

let mutable (proxy:SubjectWrapper_DoIt__Proxy) = new SubjectWrapper_DoIt__Proxy()
...
proxy.result <- Unchecked.defaultof<string>
view raw gistfile1.fs hosted with ❤ by GitHub

then shows up on the next line

let mutable (_return:int) = Wrapper.LogCall("DoIt", new Func(proxy.DoIt))
view raw gistfile1.fs hosted with ❤ by GitHub

as an error 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 '!'.; and as there are no readonly 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 proxy immutable in the F# output.

Monday, May 30, 2011

CodeDOM and poor man's lambdas -- Part 1

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 System.CodeDom.CodeSnippet* classes -- which takes away the cross-language spirit of the whole library.

So, what to do when we want to generate code containing lambdas?

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.

So, to take a case which is relevant to my interests, if we have a method (of static type Wrapper, say) --

public static T LogCall<T>(string name, Func<T> call)

that will perform some pre- and post- call logging around invoking the delegate; and we have a method

public int DoIt(int argument, out string result)

which we want to invoke through the LogCall 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:

public int DoIt(int argument, out string result)
{
string local_result = default(string);
var computed = Wrapper.LogCall("DoIt", () => wrapped.DoIt(argument, out local_result));
result = local_result;
return computed;
}
view raw gistfile1.cs hosted with ❤ by GitHub

which exposes the same interface, and delegates to the real logic in the contained object wrapped.

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 DoIt method, plus a this reference for the decorator -- and a method that stands for the lambda, which, generated names aside, looks like:

public int DoIt()
{
return this.@this.wrapped.DoIt(argument, out result);
}
view raw gistfile1.cs hosted with ❤ by GitHub

while the wrapping method looks like

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<int>(proxy.DoIt));
result = proxy.result;
return computed;
}
view raw gistfile1.cs hosted with ❤ by GitHub

And now, while the analogous F# code would probably return an int * string tuple, rather than an out parameter for multiple returns, the equivalent code snippet will do the job, just changing the types of the Func object; and the literal transposition of the C# code, out parameters and all, should still work, cross language.

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.

Func<int, int> square = x => x*x;

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.

In part 2, having simplified the problem, we can move on to using the CodeDOM to generate the LogCall decorating method and closure, given a MethodInfo.

Sunday, May 29, 2011

C# under the covers -- Enumerators

Following up on the comment stream to an earlier post, a few little bits revealed by the investigations, with a little help from ILSpy:

yield break;

is the equivalent of

return;

inside an iterator function -- by which I mean that any silent exit from the method is compiled to the equivalent of an explicit yield break;, and

foreach(T x in y) { ... }

on exit from the iteration calls the Dispose() method of the IEnumerator<T> it uses implicitly to iterate over the collection y; whereas, of course, explicit use of the enumerator MoveNext() and Current do not.

The behavioural constraints of IEnumerable are weak enough as it is, being silent on whether replayability is part of the contract or not; this inconsistent disposal (with the IDisposable interface being added only to the generic IEnumerator<T> and not the .net 1.x vanilla version) makes guessing behaviours even worse when using LINQ methods like Skip which does the dispose via using when the iteration is exhausted

// System.Linq.Enumerable
private static IEnumerable<TSource> SkipIterator<TSource>(IEnumerable<TSource> source, int count)
{
using (IEnumerator<TSource> enumerator = source.GetEnumerator())
{
while (count > 0 && enumerator.MoveNext())
{
count--;
}
if (count <= 0)
{
while (enumerator.MoveNext())
{
yield return enumerator.Current;
}
}
}
yield break;
}
view raw gistfile1.cs hosted with ❤ by GitHub

and Take, which does it implicitly by the foreach route before the iterator has been completely drained

// System.Linq.Enumerable
private static IEnumerablelt;TSource> TakeIteratorlt;TSource>(IEnumerablelt;TSource> source, int count)
{
if (count > 0)
{
foreach (TSource current in source)
{
yield return current;
if (--count == 0)
{
break;
}
}
}
yield break;
}
view raw gistfile1.cs hosted with ❤ by GitHub

There are probably similar wonders waiting to be unearthed in the F# collections APIs as well.

Friday, May 27, 2011

Belated Reviews — Anime

Otome Youkai Zakuro

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.

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 kami 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.

Super Robot Wars OG — The Inspector

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 this guide -- and just kicking back to watch the escalating battles, goofy robots, and Itano circus.

This is what anime ought to be more like -- not the endless highschool rom-com, or cute girls being cute for the otaku to perv over.

Letter Bee REVERSE

The first season 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).

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 status quo ante after the whole secret war), or repercussion (after the threat is averted, all the elements that would disturb the status quo simply migrate to a backwater town, while Lag returns to his normal rounds as a delivery boy).

Definitely a case of better to travel hopefully that to arrive.

Level E

In what seems to be Japan's answer to Men in Black, 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.

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.

Thus kicks off the case of the long-term enemy race who are serious baseball fans, the random highschoolers who are turned into a sentai 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.

Adapted from a 15 year old manga, it stuck out from all the other titles on offer for the first quarter.

Currently watching

Mobile Suit Gundam -- and that's it.

The only one of the current season's offerings I might have tried (Hyouge Mono) 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.

Tuesday, May 24, 2011

Spring Cycling 2011

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 a couple of years ago


View Spring 2011 in a larger map

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.

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.

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.

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.

Castle from Gatehouse

Castle from Gatehouse

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.

The GAR seat

The GAR seat at Sutton Hoo

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.

Friday, May 20, 2011

CodeDOM -- generating an interface to a type

This is a fairly simple process -- reflect over the public properties and methods, and generate a matching interface member, rather like this:

let GenerateInterface (target:Type) =
let compileUnit = new CodeCompileUnit()
let scope = new CodeNamespace("Generated")
let namespaces = new HashSet<string>()
compileUnit.Namespaces.Add( scope ) |> ignore
let wrapper = new CodeTypeDeclaration("I" + target.Name + "Wrapper")
wrapper.IsInterface <- true
scope.Types.Add(wrapper) |> ignore
IncludeProperties wrapper target namespaces
IncludeMethods wrapper target namespaces
namespaces
|> Seq.sort
|> Seq.iter (fun n-> scope.Imports.Add(new CodeNamespaceImport(n)))
compileUnit
view raw gistfile1.fs hosted with ❤ by GitHub

which puts the required namespaces into order (TODO: sort System.* ahead of the rest).

The two functions look much the same, so could be factored further:

let IncludeProperties (klass:CodeTypeDeclaration) (target:Type) (namespaces:HashSet<string>) =
target.GetProperties(BindingFlags.Instance ||| BindingFlags.Public)
|> Seq.sortBy (fun p -> p.Name)
|> Seq.iter (fun p ->
let m = new CodeMemberProperty()
m.Name <- p.Name
namespaces.Add(p.PropertyType.Namespace) |> ignore
m.Type <- TypeRef(p.PropertyType)
m.HasGet <- p.CanRead
m.HasSet <- p.CanWrite
p.GetIndexParameters()
|> Seq.iter ( fun t ->
let px = new CodeParameterDeclarationExpression()
px.Direction <- FieldDirection.In
px.Name <- t.Name
namespaces.Add(t.ParameterType.Namespace) |> ignore
px.Type <- TypeRef(t.ParameterType)
m.Parameters.Add(px) |> ignore
)
if m.Parameters.Count > 0 then m.Name <- "Item"
klass.Members.Add( m ) |> ignore
)
view raw gistfile1.fs hosted with ❤ by GitHub

which special cases the indexer property; and

let IncludeMethods (klass:CodeTypeDeclaration) (target:Type) (namespaces:HashSet<string>) =
target.GetMethods(BindingFlags.Instance ||| BindingFlags.Public)
|> Seq.filter (fun x -> not x.IsSpecialName )
|> Seq.sortBy (fun p -> p.Name)
|> Seq.iter (fun p ->
let m = new CodeMemberMethod()
m.Name <- p.Name
m.ReturnType <- TypeRef(p.ReturnType)
namespaces.Add(p.ReturnType.Namespace) |> ignore
p.GetParameters()
|> Seq.iter ( fun (t:ParameterInfo) ->
let px = new CodeParameterDeclarationExpression()
px.Direction <- if t.IsIn && t.IsOut then FieldDirection.Ref
else if t.IsOut then FieldDirection.Out
else FieldDirection.In
px.Name <- t.Name
namespaces.Add(t.ParameterType.Namespace) |> ignore
px.Type <- TypeRef(t.ParameterType)
m.Parameters.Add(px) |> ignore
)
klass.Members.Add( m ) |> ignore
)
view raw gistfile1.fs hosted with ❤ by GitHub

(TODO: generics and attributes, which I don't need for my immediate use case)

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 using or open). So we have helper functions

let SysType (t:Type) =
if t = typeof<System.Object> ||
t = typeof<System.Char> ||
t = typeof<System.Boolean> ||
t = typeof<System.String> ||
t = typeof<System.Int32> ||
// also uint, float, etc... ; probably also Nullables
t = typeof<System.Void> then true
else false
let TypeRef (t:Type) =
if t.IsArray then
if SysType <| t.GetElementType() then new CodeTypeReference(t)
else new CodeTypeReference(t.Name)
else if SysType t then new CodeTypeReference(t)
else new CodeTypeReference(t.Name)
view raw gistfile1.fs hosted with ❤ by GitHub

where again, TODO handling generic types, especially collections, to get them looking pretty.

So if we use string as the type, and dump to C# we get

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);
}
}
view raw gistfile1.cs hosted with ❤ by GitHub

and in F#

namespace Generated
// Generated by F# CodeDom stuff omitted...
open System
open System.Globalization
open System.Text
type
IStringWrapper = interface
abstract Item : int -> char with get
abstract Length : int with get
abstract TrimStart : char[] -> string
...
abstract Clone : unit -> obj
end
view raw gistfile1.fs hosted with ❤ by GitHub

where interestingly, the sorted order of the inserted members is reversed.

Monday, May 16, 2011

Hello CodeDOM -- building C# and F# code from F#

A five-finger exercise in CodeDOM using the "Hello World" example from F# -- more interesting stuff to follow later:

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
[<EntryPoint>]
let main a =
let compileUnit = new CodeCompileUnit()
let samples = new CodeNamespace("Samples")
samples.Imports.Add(new CodeNamespaceImport("System"))
compileUnit.Namespaces.Add( samples ) |> ignore
let class1 = new CodeTypeDeclaration("Class1")
samples.Types.Add(class1) |> ignore
let start = new CodeEntryPointMethod();
let cs1 = new CodeMethodInvokeExpression(
new CodeTypeReferenceExpression("System.Console"),
"WriteLine", new CodePrimitiveExpression("Hello CodeDOM World!"))
start.Statements.Add(cs1) |> ignore
class1.Members.Add( start ) |> ignore
GenerateCSharpCode compileUnit |> ignore
GenerateFSharpCode compileUnit |> ignore
0
view raw gistfile1.fs hosted with ❤ by GitHub

which yields up

//------------------------------------------------------------------------------
// <auto-generated>
// 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.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Samples {
using System;
public class Class1 {
public static void Main() {
System.Console.WriteLine("Hello CodeDOM World!");
}
}
}
view raw gistfile1.cs hosted with ❤ by GitHub

which isn't StyleCop compliant, but is otherwise well formatted; and

//------------------------------------------------------------------------------
// <autogenerated>
// 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.
// </autogenerated>
//------------------------------------------------------------------------------
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
[<AutoOpen>]
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 -> 'Res) = f
type
Class1 = class
new() as this =
{
}
static member UnnamedMethod_0 () =
System.Console.WriteLine("Hello CodeDOM World!") |> ignore
end
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module __EntryPoint =
[<EntryPoint>]
let Main (args:string[]) =
Class1.UnnamedMethod_0()
0
view raw gistfile1.fs hosted with ❤ by GitHub

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.

Now it just comes down to assembling to code object graph for something practical.

Sunday, May 15, 2011

Not sure if bug...

...but I set it to the fsbugs e-mail anyway.

Let's start with this F# code:

module Test.Component
// After the Haskell type
type public Either<'TFault, 'TSuccess> =
| Left of 'TFault
| Right of 'TSuccess
with
[<CompiledName("ForEvery")>]
static member iter (fault : 'TFault -> unit) (fix : 'TSuccess -> unit) (value : Either<'TFault, 'TSuccess>) =
match value with
| Left a -> fault a
| Right b -> fix b
[<CompiledName("ForEach")>]
static member iter0 =
Either<'TFault, 'TSuccess>.iter ignore
[<CompiledName("ToOption")>]
static member toOption (value:Either<'TFault, 'TSuccess>) =
match value with
| Right y -> Some y
| _ -> None
end
view raw gistfile1.fs hosted with ❤ by GitHub

The last function maps to C# as

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<TFault, TSuccess> : IEquatable<Component.Either<TFault, TSuccess>>, IStructuralEquatable, IComparable<Component.Either<TFault, TSuccess>>, IComparable, IStructuralComparable
{
...
[CompilationSourceName("toOption")]
public static FSharpOption<TSuccess> ToOption(Component.Either<TFault, TSuccess> value)
{
if (value is Component.Either<TFault, TSuccess>.Right)
{
Component.Either<TFault, TSuccess>.Right right = (Component.Either<TFault, TSuccess>.Right)value;
TSuccess y = right.item;
return FSharpOption<TSuccess>.Some(y);
}
return null;
}
...
}
}
}
view raw gistfile1.cs hosted with ❤ by GitHub

Now change the initial F# line to be namespace rather than module...

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

So for comparison purposes, let's replace CompiledName with Obsolete, and see what we've built...

namespace Test.Component
{
[DebuggerDisplay("{__DebugDisplay(),nq}"), CompilationMapping(SourceConstructFlags.SumType)]
[Serializable]
[StructLayout(LayoutKind.Auto, CharSet = CharSet.Auto)]
public abstract class Either<TFault, TSuccess> : IEquatable<Either<TFault, TSuccess>>, IStructuralEquatable, IComparable<Either<TFault, TSuccess>>, IComparable, IStructuralComparable
{
...
[Obsolete("ToOption")]
public static FSharpOption<TSuccess> toOption(Either<TFault, TSuccess> value)
{
if (value is Either<TFault, TSuccess>.Right)
{
Either<TFault, TSuccess>.Right right = (Either<TFault, TSuccess>.Right)value;
TSuccess y = right.item;
return FSharpOption<TSuccess>.Some(y);
}
return null;
}
...
}
}
where I can't for th
view raw gistfile1.cs hosted with ❤ by GitHub

where I can't for the life of me see the difference.

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.

Friday, May 06, 2011

Fixing missing volume and network tray icons

On some Vista systems, there's an intermittent corruption of the icons that give network status and volume control in the system notification "tray".

Running this PowerShell script will clear the wedged icons and restart explorer