MSDN Magazine - March 2009 - (Page 34) Figure 5 Let l1 = [1; 2; 3;] IL_0000: ldc.i4.1 IL_0001: ldc.i4.2 IL_0002: ldc.i4.3 IL_0003: call class [FSharp.Core]Microsoft.FSharp.Collections. List'1 class [FSharp.Core]Microsoft.FSharp.Collections. List'1 ::get_uniq_Empty() IL_0008: newobj instance void class [FSharp.Core]Microsoft.FSharp. Collections.List'1/_Cons ::.ctor(!0, class [FSharp.Core]Microsoft.FSharp.Collections.List'1 ) IL_000d: newobj instance void class [FSharp.Core]Microsoft.FSharp. Collections.List'1/_Cons ::.ctor(!0, class [FSharp.Core]Microsoft.FSharp.Collections.List'1 ) IL_0012: newobj instance void class [FSharp.Core]Microsoft.FSharp. Collections.List'1/_Cons ::.ctor(!0, class [FSharp.Core]Microsoft.FSharp.Collections.List'1 ) a function as a parameter and applies it to each element in the list, generating either a single result (called a “fold” operation) or a new list containing the results of each (our “map”). Notice in Figure 4 how this turns into a rather complex bit of IL after compilation. Again, the fact that it maps to a public static method on the class Module is not surprising; what will make this difficult to interact with from C#, however, is that the static method fact that I could do this easily enough from within C# is irrelevant here—I have to figure out how to do the simple things before I can tackle the complex.) To call this from C# means that several things are going to have to happen successfully: the input collection will need to be converted to an F# list type; the function applied to each element must be converted into an F# “FastFunc” instance; and the returned F# list will need to be converted into a type C# can use, or else used in its native F# form directly. The C# code is going to need those F# types, and so the first step will be to add the appropriate F# assembly reference, in this case, FSharp.Core.dll. Constructing an F# list, however, is not like constructing a C# List—rather than passing in the C# collection via a constructor, F# assumes that lists are built up using the “cons” operator, which is a static method on the F# List class. In other words, the F# code let l1 = [1; 2; 3;] One of the greatest strengths of F# is that it treats functions as first-class values. takes F# lists (meaning, type-parameterized instances of Microsoft. FSharp.Collections.List) as input and return types, along with a function (meaning, an instance of a dually-type-parameterized instance of Microsoft.FSharp.Core.FastFunc) as the second input. This is going to require some serious C# code to work correctly. For this example, I want some C# code to take a collection of integers, and convert them into their corresponding string forms. (The Figure 6 The Original Code Plus Modifications open System open System.IO open Microsoft.FSharp.Control.CommonExtensions #nowarn "057" let aCountSpace filename size = async { let space = Convert.ToByte ' ' use stream = File.OpenRead (filename) let bytes = Array.create size space let! nbytes = stream.ReadAsync (bytes,0,size) let count = bytes |> Array.fold_left (fun acc x -> if (x=space) then acc + 1 else acc) 0 return count } let aCounted (files : FileInfo array) = files |> Array.map (fun f -> aCountSpace (f.FullName) (int f.Length)) |> Async.Parallel |> Async.Run turns into the rather ugly IL shown in Figure 5. This means that to convert a C# array into an F# list suitable for passing in to the mymap method, I have to repeatedly call the static Cons method on List, passing in the new head each time; note that because the item added goes to the head of the list, to keep the F# list in the same order as the C# array, I have to start at the end of the array and work backward: int[] scores = {1, 2, 3, 4, 5}; var fs_scores = Microsoft.FSharp.Collections.List .get_uniq_Empty(); for (int i = scores.Length-1; i >= 0; i--) { fs_scores = Microsoft.FSharp.Collections.List .Cons(scores[i], fs_scores); } Already this is becoming something of a pain. Obtaining an instance of F#’s FastFunc type is even more tedious because the FastFunc type, like the core System.Delegate type, isn’t really intended to be instantiated by programmers, but instead handled by the F# compiler. Ordinarily, when constructing an instance of this in F# code, the compiler actually generates an inner class that inherits from FastFunc, something you could do from C#, but that would require a lot more work. Suddenly calling into this F# function seems like way too much work for the benefit derived. What all of this serves to illustrate is that the mapping of a language to its underlying platform is crucial to deciding where and how to use languages in a polyglot manner. Something that is trivial to do inside of F#, for example, can turn out to be difficult to do directly from C#. This doesn’t mean giving up on the idea of polyglotism, it just means you must carefully choose which languages you use together and for what purposes. From a more positive perspective, consider, one of the examples from Chance’s October article with additional code at the bottom (see Figure 6) Note that I’ve modified it slightly. However, the only modification I made here was to specify the “files” argument in the “aCounted” method to be an array of FileInfo, something which was inferred by the F# compiler in Chance’s original code. The IL signature for “aCounted” now looks like .method public static int32[] aCounted(class [mscorlib]System. IO.FileInfo[] files) cil managed 34 msdn magazine The Polyglot Programmer
For optimal viewing of this digital publication, please enable JavaScript and then refresh the page. If you would like to try to load the digital publication without using Flash Player detection, please click here.