Welcome to MSDN Blogs Sign in | Join | Help

Programmatically Resolve Assembly Name to Full Path the Same Way MSBuild Does

Jomo Fisher—Every once in a while I find I need to turn and assembly name like “System” or “System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL” into a path to the actual file for that assembly. This is a tricky problem considering the assembly name might be for a product install in Program Files or elsewhere. MSBuild already has rules for doing this resolution. Here’s how to call that code directly (written in F#):

#light

 

#r "c:\Windows\Microsoft.NET\Framework\v2.0.50727\Microsoft.Build.Tasks.dll"

#r "c:\Windows\Microsoft.NET\Framework\v2.0.50727\Microsoft.Build.Utilities.dll"

#r "c:\Windows\Microsoft.NET\Framework\v2.0.50727\Microsoft.Build.Framework.dll"

#r "c:\Windows\Microsoft.NET\Framework\v2.0.50727\Microsoft.Build.Engine.dll"

open Microsoft.Build.Tasks

open Microsoft.Build.Utilities

open Microsoft.Build.Framework

open Microsoft.Build.BuildEngine

 

/// Reference resolution results. All paths are fully qualified.

type ResolutionResults = {

    /// Paths to primary references

    referencePaths:string array

    /// Paths to dependencies

    referenceDependencyPaths:string array

    /// Paths to related files (like .xml and .pdb)

    relatedPaths:string array

    /// Paths to satellite assemblies used for localization.

    referenceSatellitePaths:string array

    /// Additional files required to support multi-file assemblies.

    referenceScatterPaths:string array

    /// Paths to files that reference resolution recommend be copied to the local directory

    referenceCopyLocalPaths:string array

    /// Binding redirects that reference resolution recommends for the app.config file.

    suggestedBindingRedirects:string array

    }

 

 

let Resolve(references:string array, outputDirectory:string) =

    let x = { new IBuildEngine with

                member be.BuildProjectFile(projectFileName, targetNames, globalProperties, argetOutputs) = true

                member be.LogCustomEvent(e) = ()

                member be.LogErrorEvent(e) = ()

                member be.LogMessageEvent(e) = ()

                member be.LogWarningEvent(e) = ()

                member be.ColumnNumberOfTaskNode with get() = 1

                member be.ContinueOnError with get() = true

                member be.LineNumberOfTaskNode with get() = 1

                member be.ProjectFileOfTaskNode with get() = "" }

 

    let rar = new ResolveAssemblyReference()

    rar.BuildEngine <- x

    rar.TargetFrameworkDirectories <- [|@"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5\"|]

    rar.AllowedRelatedFileExtensions <- [| ".pdb"; ".xml"; ".optdata" |]

    rar.FindRelatedFiles <- true

    rar.Assemblies <- [|for r in references -> new Microsoft.Build.Utilities.TaskItem(r):>ITaskItem|]

    rar.SearchPaths <- [| "{CandidateAssemblyFiles}"

                          "{HintPathFromItem}"

                          "{TargetFrameworkDirectory}"

                          "{Registry:Software\Microsoft\.NetFramework,v3.5,AssemblyFoldersEx}"

                          "{AssemblyFolders}"

                          "{GAC}"

                          "{RawFileName}"

                          outputDirectory |]

                             

    rar.AllowedAssemblyExtensions <- [| ".exe"; ".dll" |]    

    rar.TargetProcessorArchitecture <- "x86"                    

    if not (rar.Execute()) then

        failwith "Could not resolve"

    {

        referencePaths = [| for p in rar.ResolvedFiles -> p.ItemSpec |]

        referenceDependencyPaths = [| for p in rar.ResolvedDependencyFiles -> p.ItemSpec |]

        relatedPaths = [| for p in rar.RelatedFiles -> p.ItemSpec |]

        referenceSatellitePaths = [| for p in rar.SatelliteFiles -> p.ItemSpec |]

        referenceScatterPaths = [| for p in rar.ScatterFiles -> p.ItemSpec |]

        referenceCopyLocalPaths = [| for p in rar.CopyLocalFiles -> p.ItemSpec |]

        suggestedBindingRedirects = [| for p in rar.SuggestedRedirects -> p.ItemSpec |]

    }

   

try

    let s = Resolve([| "System"

                       "System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL"

                       "Microsoft.SqlServer.Replication"

                    |], "")

    printfn "%A" s

finally

    ignore (System.Console.ReadKey())

The result of running the code looks like this:

{referencePaths =

  [|"C:\\Program Files\\Microsoft SQL Server\\90\\SDK\\Assemblies\\Microsoft.SqlServer.Replication.dll";

    "C:\\Program Files\\Reference Assemblies\\Microsoft\\Framework\\v3.5\\System.Core.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\System\\2.0.0.0__b77a5c561934e089\\System.dll"|];

 referenceDependencyPaths =

  [|"C:\\Windows\\assembly\\GAC_MSIL\\System.DirectoryServices\\2.0.0.0__b03f5f7f11d50a3a\\System.DirectoryServices.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\System.Xml\\2.0.0.0__b77a5c561934e089\\System.Xml.dll";

    "C:\\Windows\\assembly\\GAC_32\\Microsoft.SqlServer.BatchParser\\9.0.242.0__89845dcd8080cc91\\Microsoft.SqlServer.BatchParser.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\System.Runtime.Remoting\\2.0.0.0__b77a5c561934e089\\System.Runtime.Remoting.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\Microsoft.Vsa\\8.0.0.0__b03f5f7f11d50a3a\\Microsoft.Vsa.dll";

    "C:\\Windows\\assembly\\GAC_32\\System.Data.OracleClient\\2.0.0.0__b77a5c561934e089\\System.Data.OracleClient.dll";

    "C:\\Program Files\\Microsoft SQL Server\\90\\SDK\\Assemblies\\Microsoft.SqlServer.ConnectionInfo.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\Accessibility\\2.0.0.0__b03f5f7f11d50a3a\\Accessibility.dll";

    "C:\\Windows\\assembly\\GAC_32\\System.Transactions\\2.0.0.0__b77a5c561934e089\\System.Transactions.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\System.DirectoryServices.Protocols\\2.0.0.0__b03f5f7f11d50a3a\\System.DirectoryServices.Protocols.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\System.Drawing\\2.0.0.0__b03f5f7f11d50a3a\\System.Drawing.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\System.Deployment\\2.0.0.0__b03f5f7f11d50a3a\\System.Deployment.dll";

    "C:\\Program Files\\Microsoft SQL Server\\90\\SDK\\Assemblies\\Microsoft.SqlServer.Smo.dll";

    "C:\\Windows\\assembly\\GAC_32\\System.EnterpriseServices\\2.0.0.0__b03f5f7f11d50a3a\\System.EnterpriseServices.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\System.Web.RegularExpressions\\2.0.0.0__b03f5f7f11d50a3a\\System.Web.RegularExpressions.dll";

    "C:\\Program Files\\Microsoft SQL Server\\90\\SDK\\Assemblies\\Microsoft.SqlServer.WmiEnum.dll";

    "C:\\Program Files\\Microsoft SQL Server\\90\\SDK\\Assemblies\\Microsoft.SqlServer.ServiceBrokerEnum.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\System.Configuration\\2.0.0.0__b03f5f7f11d50a3a\\System.Configuration.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\System.Web.Services\\2.0.0.0__b03f5f7f11d50a3a\\System.Web.Services.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\System.Management\\2.0.0.0__b03f5f7f11d50a3a\\System.Management.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\System.Design\\2.0.0.0__b03f5f7f11d50a3a\\System.Design.dll";

    "C:\\Windows\\assembly\\GAC_32\\System.Web\\2.0.0.0__b03f5f7f11d50a3a\\System.Web.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\System.Windows.Forms\\2.0.0.0__b77a5c561934e089\\System.Windows.Forms.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\System.Security\\2.0.0.0__b03f5f7f11d50a3a\\System.Security.dll";

    "C:\\Program Files\\Microsoft SQL Server\\90\\SDK\\Assemblies\\Microsoft.SqlServer.SmoEnum.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\System.Runtime.Serialization.Formatters.Soap\\2.0.0.0__b03f5f7f11d50a3a\\System.Runtime.Serialization.Formatters.Soap.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\Microsoft.JScript\\8.0.0.0__b03f5f7f11d50a3a\\Microsoft.JScript.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\System.Configuration.Install\\2.0.0.0__b03f5f7f11d50a3a\\System.Configuration.Install.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\Microsoft.VisualC\\8.0.0.0__b03f5f7f11d50a3a\\Microsoft.VisualC.dll";

    "C:\\Program Files\\Microsoft SQL Server\\90\\SDK\\Assemblies\\Microsoft.SqlServer.SqlEnum.dll";

    "C:\\Windows\\assembly\\GAC_32\\System.Data\\2.0.0.0__b77a5c561934e089\\System.Data.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\System.Drawing.Design\\2.0.0.0__b03f5f7f11d50a3a\\System.Drawing.Design.dll";

    "C:\\Program Files\\Microsoft SQL Server\\90\\SDK\\Assemblies\\Microsoft.SqlServer.Rmo.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\System.Data.SqlXml\\2.0.0.0__b77a5c561934e089\\System.Data.SqlXml.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\System.ServiceProcess\\2.0.0.0__b03f5f7f11d50a3a\\System.ServiceProcess.dll";

    "C:\\Program Files\\Microsoft SQL Server\\90\\SDK\\Assemblies\\Microsoft.SqlServer.RegSvrEnum.dll"|];

 relatedPaths = [||];

 referenceSatellitePaths = [||];

 referenceScatterPaths =

  [|"C:\\Windows\\assembly\\GAC_32\\System.EnterpriseServices\\2.0.0.0__b03f5f7f11d50a3a\\System.EnterpriseServices.Wrapper.dll"|];

 referenceCopyLocalPaths = [||];

 suggestedBindingRedirects = [||];}

This posting is provided "AS IS" with no warranties, and confers no rights.

 

Posted by Jomo Fisher | 3 Comments
Filed under: ,

Correct by Construction in F#

Correct by Construction in F#

Jomo Fisher—a theme in the design of the F# language is that problems in the code are revealed as compilation errors. Consider this C# code which is used to compose a courteous letter to a customer:

enum Courtesy { Mr, Ms, Dr }

class Customer {

    public Courtesy Courtesy { get; set; }

    public string Name { get; set; }

}

class Letter {

    public string Introduction { get; set; }

    public string Body { get; set; }

}

static Letter Correspond(Customer customer, string message) {

    switch (customer.Courtesy)

    {

        case Courtesy.Mr:

            return new Letter { Introduction = "Mr. " + customer.Name,

                                Body = message };

        case Courtesy.Ms:

            return new Letter { Introduction = "Ms. " + customer.Name,

                                Body = message };

    }

    return null;

}

 

This compiles without errors despite having some problems (can you spot them?)

I want to stop now and claim that I’m not picking on C# which is a wonderful language. It’s just that I need to compare F# to something in order to illuminate my point and I happen to know C# pretty well.

Now, transliterate the code above into F#:

#light

type Courtesy = Mr | Ms | Dr

type Customer = {

    Courtesy:Courtesy

    Name:string

}

type Letter = {

    Introduction:string

    Body:string

}

let Correspond(customer,message) =

    match customer.Courtesy with

    | Mr -> {Introduction = "Mr. " + customer.Name; Body=message}

    | Ms -> {Introduction = "Ms. " + customer.Name; Body=message}

    | _ -> null

 

Compiling this gives a warning on the last line:

error FS0043: The type 'Letter' does not have 'null' as a proper value.

In C#, objects are allowed to be null by default and your code is responsible for handling the possibility of nullity everywhere. F# objects are not allowed to be null and, except in a few special cases such as interoperating with non-F# code, you can assume that object instances aren’t null.

So the caller of the Correspond function need not worry about receiving a null Letter instance and I can just delete the last line:

let Correspond(customer,message) =

    match customer.Courtesy with

    | Mr -> {Introduction = "Mr. " + customer.Name; Body=message}

    | Ms -> {Introduction = "Ms. " + customer.Name; Body=message}

 

Now I get a warning:

warning FS0025: Incomplete pattern match on expression. The value ‘Dr’ will not be matched

This is an easy mistake to make: I forgot one of the cases in the Courtesy enumeration.

An aside on pattern matching: If you code in F# you’ll find yourself using pattern matching for a significant amount of the logic in your program. It’s far more than just a synonym for switch. It can do complex matching against just about any type of object. This, combined with completeness checking, helps eliminate a large class of bug.

Here’s my final corrected code:

let Correspond(customer,message) =

    match customer.Courtesy with

    | Mr -> {Introduction = "Mr. " + customer.Name; Body=message}

    | Ms -> {Introduction = "Ms. " + customer.Name; Body=message}

    | Dr -> {Introduction = "Dr. " + customer.Name; Body=message}

 

This idea that F# should lead you to write code that is correct by construction is not just about the features I mentioned above. Its fundamentally weaved through the language. Take a look at how casting works for another example.

Honestly, it took me some time to get used to. I particularly struggled with the non-nullity of object instances. I was used to being able to smuggle the special “null” value around without changing my code to accommodate it. I realize now I was just sweeping a potential problem under the rug.

This posting is provided "AS IS" with no warranties, and confers no rights.

 

Posted by Jomo Fisher | 7 Comments
Filed under: ,

Strange Confluence: An Immutable Queue in F#

Jomo Fisher--Reading one of my favorite blogs this morning--Eric Lippert's Fabulous Adventures in Coding--I came across his article on implementing an immutable queue in C#. The funny thing is that just yesterday I wrote exactly the same structure in F#. Here it is for your reading pleasure:

    type Fifo<'a> =

        new()={xs=[];rxs=[]}

        new(xs,rxs)={xs=xs;rxs=rxs}

       

        val xs: 'a list;

        val rxs: 'a list;

   

        static member Empty() = new Fifo<'a>()

        member q.IsEmpty = (q.xs = []) && (q.rxs = [])

        member q.Enqueue(x) = Fifo(q.xs,x::q.rxs)

        member q.Take() =

            if q.IsEmpty then failwith "fifo.Take: empty queue"

            else match q.xs with

                    | [] -> (Fifo(List.rev q.rxs,[])).Take()

                    | y::ys -> (Fifo(ys, q.rxs)),y

This is code modified from a mutable structure that I found in the F# distribution. The queues are pretty similar except for a few minor details:

  • F# version doesn't implement IEnumerable<T>.
  • F# version relies on the built-in list class which is semantically identical to an immutable stack.
  • C# version carries the "popped" instance of T along with the queue itself whereas the F# version returns this as part of a tuple from Take(). I think I prefer my way on this point--it seems like information leakage for the queue have a memory of the last thing popped.

That's all for now, and as always...

 

This posting is provided "AS IS" with no warranties, and confers no rights.

Posted by Jomo Fisher | 1 Comments
Filed under: , , ,

Tight Code--A Puzzle in F#

Jomo Fisher--Luke Hoban wrote something in a blog entry that resonated with me:

One of the most striking features of F# code is that it is very terse - ideas can typically be expressed with a small amount of code. 

Don Syme once mentioned (I'm paraphrasing) that an aspirational goal for F# in the beginning was to make a compiler that could compile itself in less than 10,000 lines of code. Though that line count has since been passed I often get the sense that, with enough thought, I could boil whatever code I'm working on down to almost nothing.

Consider this coding problem (which I have since used as an interview question). Take a singly-linked-list of singly-linked-lists and pivot the inner values. For example say I have this list:

let have = [['a';'b';'1'];['c';'d';'2'];['e';'f';'3']]

Then the list I want is this:

let want = [['a';'c';'e'];['b';'d';'f'];['1';'2';'3']]

So the new list's first list has the first item from each of the original inner lists and so on. This problem actually came up in real-world piece of code I was working on. My first cut was procedural and about twenty lines of code. I don't have it here to show you, but after a few iterations and refactorings I got it down to a respectable nine lines:

let flatteninto source target =

    List.zip target source |> List.map(fun (head, tail)->head@[tail])

let pivot list =

    let rec work target = function

          head::tail-> work (flatteninto head target) tail

        | [] -> target

    let empties = list|>List.map(fun l->[])

    work empties list

At this point, I was stuck. It worked, but it seemed like there should be a better solution. In particular, the second to last line where I build up a list of empty lists didn't seem quite right. Being stuck, I asked my teammates whether there was a better solution out there. As it turns out, James Margetson had seen a beautiful four line solution to the problem.

I'll post the solution James showed me in a few days. In the meantime, I'd like to invite you to give the problem a try in the programming language of your choice. Can you cleanly beat my nine-line solution above? Can you get it down to four lines? I only ask that you don't change the input structure--singly-linked-list of singly-linked-list. Also, in my problem, the input was guaranteed to be well formed so I didn't need to check for jagged inner lists or other malformed inputs. Finally, notice that while I showed a 3x3 input in the example the dimensions don't have to be the same.

Update 11/20/2007 - Spoiler Alert!

As promised, here's the F# that James showed me

let rec transpose haves =

    match haves with

    | (_::_)::_ -> map hd haves :: transpose (map tl haves)

    | _         -> []

This is essentially the same as the Haskell algorithm that has been posted in the comments.

I want to thank the folks who posted solutions in various different languages--Scheme, Haskell, Ruby, C#, Python. Also, there was an OCaml solution which is indeed compatible with F#.

Several folks pointed out the analogy with matrix transpose. The code does get a lot easier when your input is an array of arrays, but you don't always get to pick your input representation.

One commenter opined that functional code may be easier to read than it is to write. This is true for me in one sense: there seems to always be a way to write the code a little better. Figuring out when I'm done is a challenge.

 

This posting is provided "AS IS" with no warranties, and confers no rights.

Posted by Jomo Fisher | 36 Comments
Filed under: ,

The Least a C# Programmer Needs to Know about F# Part II--Modules

Jomo Fisher--Many languages, especially those in the OO vein, require an outermost class to put code in. Usually, good practice requires an enclosing namespace as well.  F# allows functions and even function calls in the outermost scope. Here is the minimal F# program:

 

printf "Hello!"

 

Really, that's the whole thing. My first concern upon seeing this was that the all these functions in the global namespace would eventually collide in larger projects making things unmanageable. It seemed a lot like returning to the nasty global state that you sometimes see in older C codebases. It turns out, however, that F# puts each file's functions, function calls and types inside its own construct called a "Module". Consider this slightly modified version:

 

// File Hello.fs

#light

let Say s = printf s

Say "Hello!"

 

Which is saved in file called Hello.fs. This defines a function called 'Say' and then calls it. Using Reflector, I can see the Say method decompiled into C#:

 

[CompilationMapping(SourceLevelConstruct.Module)]

public class Hello

{

    public static T Say<T>(Format<T, TextWriter, Unit, Unit> s)   {

        return Pervasives.printf<T>(s);

    }

}

 

The F# compiler has put the method into a public class called Hello . This is F#'s representation of the Module. The class name Hello is derived from the file name Hello.fs.  You can change the Module name if you like:

 

module MyModule =

    let Say s = printf s

    Say "Hello!"

 

For larger projects, I think its best to explicitly name modules. For smaller, one-off projects its convenient not to have to think about it.

 

In most .NET projects I've worked on, there usually ends up being a few utility classes that just have a bunch of related static methods in them. These are functions that just didn't cleanly fit the OO paradigm. Its tempting to think of these classes as code smells. After all, surely there's a clean OO representation for them that I haven't thought of yet, right?

 

I think the answer to this is: no, not everything fits well into object-orientation.

 

My proof by existence is to consider .NET's Stream classes that were released in .NET 1.0. These classes were extremely well factored. They beautifully hid the underlying media or communication protocol behind an abstract stream of information.  They were also damn hard to use for simple cases. In a later .NET release the File, Path and Directory classes were introduced. These were just classes with nice, simple static methods for doing the common things you want to do with files, paths and directories.

 

F#  makes this style of API a first-class citizen: the Module.

 

So am I saying good-bye to OO? No way. But I consider it a hammer and not every problem is a nail.

 

This posting is provided "AS IS" with no warranties, and confers no rights.

Posted by Jomo Fisher | 3 Comments
Filed under:

WideFinder--Naive F# Implementation

Jomo Fisher--Here's an interesting problem that some people are having fun with. Don Box posted a naive implementation in C# so I thought I'd post the equivalent in F#: 

#light

open System.Text.RegularExpressions

open System.IO

open System.Text

 

let regex = new Regex(@"GET /ongoing/When/\d\d\dx/(\d\d\d\d/\d\d/\d\d/[^ .]+)", RegexOptions.Compiled)

 

let seqRead fileName =

    seq { use reader = new StreamReader(File.OpenRead(fileName))

          while not reader.EndOfStream do

              yield reader.ReadLine() }

             

let query fileName =

    seqRead fileName

    |> Seq.map (fun line -> regex.Match(line))

    |> Seq.filter (fun regMatch -> regMatch.Success)

    |> Seq.map (fun regMatch -> regMatch.Value)

    |> Seq.countBy (fun url -> url)

And here's the code to call it:   

for result in query @"file.txt" do

    let url, count = result

One nice thing is that F#'s interactive window has a #time;; option which shows you wall-clock time and CPU time. Here is the result from running the code above on a 256meg file I concatenated together (I couldn't find the one Don was using):

Real: 00:00:06.899, CPU: 00:00:04.165, GC gen0: 416, gen1: 1, gen2: 0

It looks like the majority of the time is in CPU so there should be ample opportunity to parallelize. One thing to note: I think the interactive window is unoptimized--when I just compile and run the code, I get times in the sub 5-seconds range. My machine is a 4-way 2.4 GHz Core Duo.

This posting is provided "AS IS" with no warranties, and confers no rights.


                    
				            
Posted by Jomo Fisher | 4 Comments
Filed under:

The Least a C# Programmer Needs to Know about F# Part I--Implicit Types

Jomo Fisher--A few weeks ago, a fellow C# programmer asked me what the biggest differences between programming in C# and programming in F# are. Since then, I've been building a list of differences. My plan was to write a single article that discussed everything. This morning the list got too long to reasonably put in a single article. Also, I'm not sure when I'll be done discovering the differences. So here's my first in a series of articles. (This is based on the 3.0 version of the C# language. If you want to see what's new in C#, it might be helpful to start here.)

 

You rarely mention types in F# code

In version 3.0, C# added the var keyword. This keyword allows you to not specify a type and instead rely on the compiler to figure out the type.  This works for locals only so most types are still explicitly specified in the code. In F#, this balance is reversed. You can explicitly specify types but you're rarely required to. F# has a powerful system for deducing the types. Consider this F# function:

 

let Add a b = a + b

 

This defines a function which adds two of something to each other. So what are the types of the variables a and b? The F# compiler has chosen int because of the addition that's happening in the function. A second function:

 

let AddPlusOne a b = a + b + 1.1

 

looks similar, but now the a, b and the return of the function are types as float. In this case, the compiler decides float is the right type because we're adding constant float value.

 

Most of the time in F# you don't specify types. I find myself relying heavily on the tooltips in Visual Studio for type information:

image

I think its legitimate to be uncomfortable with this level of implicitness. Especially coming from the C++ diaspora there is a tradition of being explicit when possible. Indeed, in F# you are free to explicitly specify types everywhere if you choose.

 

That said, I really like C#'s type inference and have used it in production code (LINQ to SQL). It tends to make the code more readable because you spend more time looking at the algorithm and less time looking at concrete type names.  I haven't shipped or maintained F# code yet, but my feeling is that more type inference is better.

 

 

 

This posting is provided "AS IS" with no warranties, and confers no rights.

 

Posted by Jomo Fisher | 6 Comments
Filed under: ,

Soma on F#

Soma announced some exciting news this morning. Developer Division--the people at Microsoft who make Visual Studio--is partnering with Microsoft Research on F#. We're going to fully integrate F# into Visual Studio:

“This is one of the best things that has happened at Microsoft ever since we created Microsoft Research over 15 years ago.  Here is another great example of technology transfer at work.  We will be partnering with Don Syme and others in Microsoft Research to fully integrate the F# language into Visual Studio and continue innovating and evolving F#.”

Don Syme and his team work out of Cambridge. Over the past few weeks we've been busily spinning up a team in Redmond to work with them. As its stands today, the team looks like this:

Cambridge: Don Syme (does everything), James Margetson (development) and Ralf Herbrich (development)

Redmond: Luke Hoban (PM), Me (development)

Of course we'll add more people over the next weeks and months. Critically, we still need to build the test organization.

The C# Product Unit is sponsoring the partnership and for the foreseeable future Luke and I will continue to work in the C# organization. I think this is a strong match because the C# org, as you probably expect, has a deep relationship with the other parts of Microsoft that will be important going forward: Parallel Fx (PFx), the CLR team, and so forth. We also have Anders Hejlsberg.

F# is a language that makes it easy to make beautiful, concise code. I'm excited to be part of getting it into people's hands.

This posting is provided "AS IS" with no warranties, and confers no rights.

Posted by Jomo Fisher | 6 Comments

Adventures in F#--Sweet Test-First Kung Fu

Jomo Fisher--Up until now, I've been avoiding using F# with the VS IDE. I've been using notepad.exe and fsc.exe because I wanted to build my own expectation for what the experience should be before I experienced what it actually was. I can tell you that I didn't expect the sweet experience of using the F# interactive window.

I've written in the past about Test Driven Development. Regardless of language, I really like the process of iteratively writing code and unittests tests for that code. For very small projects--simple algorithms and classes--I've found the process of getting going very tedious compared to the amount of actual code I want to write. In order to get started, you need a new project and solution for your code, a separate project for your tests and you need to reference your code project from your test project. All this means friction and tends to discourage firing up VS and freely inventing for very small projects.

Enter the F# Interactive window. Onc