Easy Async
Build Concurrent Apps From Simple F# Expressions
Chance Coble
This article is based on prerelease versions of the Visual Studio add-in for F# and related technologies. All information herein is subject to change.
This article discusses:
|
This article uses the following technologies: F# |
Contents
The Problem of Traditional Notation
Functional Programming
Asynchronous Expressions
Using let! and return!
Primitives
Create Your Own Primitives
Primitives and Supporting Functions
Extending Async Calls to the Web
Working with Other .NET Languages
Asynchronous programming is considered so difficult by many programmers that they create synchronous version of programs that could run more efficiently as asynchronous applications just to avoid the assumed complexity of asynchronous code.
Until recently those choices had little consequence. However, with new opportunities in distributed computing such as multicore processors and service-oriented architectures, developers are beginning to feel the burden. A classic example of this is in I/O operations, where processor time could be employed more profitably while an I/O-bound operation is taking place. Plenty of applications exist where many files are read and processed or where a complex orchestration of Web services is called upon to retrieve data that is processed and then sent to another service.
In reality, an asynchronous program is a simpler arrangement than a synchronous one. An asynchronous program purposefully says nothing about the sequence of the commands to be executed. It is a lazy specification and thus should be simpler to define.
One stumbling block that developers encounter with asynchronous programming is that they become so concerned with getting concurrency right that they forget the core simplicity of the program. Locks, semaphores, and other concurrent computing tools tend to focus the program's layout less around its core logic and more around the concurrent management of its resources.
That is where you will see the value of asynchronous expressions in F#. You can separate simple programs from the concurrent control flow and reveal the simplicity and readability of your core program. Additionally, one of the most powerful aspects of the F# language is its ability to create libraries that can be called seamlessly from any other language compliant with the Microsoft .NET Framework, allowing you to leverage the intuitive parallelism provided by F# asynchronous expressions from within existing imperative C# or Visual Basic code.
In this article, I will demonstrate the essence of asynchronous expressions in F#. I will provide implementation examples and even show you how to incorporate your own frameworks into simple asynchronous primitives. Using the techniques I cover here, you will be able to create modular and expressive programs for your most challenging concurrency problems.
The Problem of Traditional Notation
Here's a traditional example of calling methods or functions:
>commandOne();
commandTwo();
commandThree();
There is no question in this traditional notation that commandTwo is preceded by commandOne, and that it will be succeeded by commandThree only after it completes.
Trying to specify that you don't care what order these execute in is more difficult in this notation. It should be no more complex than a simple delimiter between them:
commandOne() | commandTwo() | commandThree()
Something like this could have been a useful notation for the three commands when you don't care what order they execute in. Perhaps even more useful would be a construct that indicates how you want them to run instead of a delimiter, such as this:
async {commandOne()}; async {commandTwo()}; async {commandThree()}
And suppose you want to set a value to the result of the first one that completes:
x = first(
async { commandOne() };
async { commandTwo() };
async { commandThree()}; )
This seemingly simple instruction becomes a major effort in most popular paradigms.
From this perspective, the semicolon begins to look less like statement termination and more like an operator that blocks the execution of the following statement until the current statement has completed. That a synchronous program is a more specific definition of a program should be intuitive from this perspective.
Many advances in programming language design are shedding new light on the problem of asynchronous program design. By making it a programming language problem you immediately see ways to construct a notation that is much more expressive, clear, and concise. Using asynchronous expressions, the F# team has not only moved closer toward that goal, but has also facilitated a level of interaction between the programmer and the programming language to which the .NET community has not yet had access.
Functional Programming
You may have already seen a lot of F# in Ted Neward's primer ( msdn.microsoft.com/magazine/cc164244 ). For the benefit of those new to this language, I will review a couple of key points, but only those required for an introduction to asynchronous expressions in F#.
This article uses the #light compiler directive for all code examples, even though it may not be explicit. That directive will cause the compiler to parse each line of code based on its alignment with the other code. While this results in a clear layout, it also makes white space an important part of the code body. When you explore with the code in this article yourself, be sure to follow the alignment used in the examples with spaces rather than tabs. To use the #light directive, place it above the code it is to influence:
#light
System.Console.WriteLine "Hello World"
All of the code included with this article can be compiled, but for easy execution consider using the F# Interactive Console. To start this console, open Visual Studio. Click View | Other Windows | F# Interactive.
An important aspect of F# is that everything is a value, even a function. By default, everything is an immutable value. For example, consider the following line of code:
let x = 2
This creates a value 2 that you can refer to as x. The value x cannot change to become something else later. Perhaps in another scope x could refer to something else entirely, but it cannot change in this scope. Values can also be created as the result of expressions. The following line would bind the result 5 to x:
let x = 2 + 3
You can almost begin to interpret the equals sign less as assignment in this setting, and more as an assertion that the right-hand side equals the left-hand side. This value setting also works for functions. If I want to write to the console frequently I can create an abbreviated value emit that represents the Console class's WriteLine method for strings:
let emit:string->unit = System.Console.WriteLine
The value emit now refers to the console method. After the colon there is a type signature for the function. This type signature says the function accepts a string and returns unit (unit is like void in C#). Again, the value emit isn't mutable and cannot represent anything else in this scope. So you can see that in F#, methods and functions are values.
Now that I have set a value to a function, it would be nice to know how to invoke them. After all, that is how we actually run programs, isn't it?
In functional programming, we use the term application rather than invocation. This is simply the application of functions to arguments. In F#, a function is not invoked until it has been applied to all of its arguments.
Applying a function to its arguments has a slightly different look in a functional setting. You apply functions to values so frequently in functional programming that the least intrusive operator is used to indicate application: the space. For example, to apply emit, which represents the function Console.WriteLine, you would write:
emit "Hello World"
That code would result in the following output:
Hello World
Alternatively, you could have written this:
emit("Hello World")
C# developers will sometimes find this flexibility confusing because it looks like their familiar method invocation. The parentheses are only present to group the arguments, and that is why the compiler allows the exclusion of the space. In this article, the convention will be to use parentheses only when required to group arguments and terms together.
Finally, some functions take one argument of void. In F# this is referred to as unit and is represented by the empty parentheses:
let emitter () = emit "Hello World"
emitter ()
This code results in the following output:
"Hello World"
Asynchronous Expressions
Now you are ready for a peek at an asynchronous expression. Figure 1 shows a synchronous program that reads text files in a directory and counts the space characters in each file. The function countSpace takes a file name and size and opens a stream to read in the appropriate number of bytes. It then counts every byte that is a space in a list-folding operation. Below that, the value "files" is the array of text files in that directory and the value "counted" computes the function countSpace for each of the files using Array.map.
Figure 1 Counting Spaces Synchronously
#light
open System.IO
open System
let countSpace filename size =
let space = Convert.ToByte ' '
use stream = File.OpenRead filename
let bytes = Array.create size space
let nbytes = stream.Read (bytes,0,size)
let count =
bytes
|> Array.fold_left (fun acc x -> if (x=space) then acc + 1 else acc) 0
count
let files = (DirectoryInfo @"C:\Users\Chance\Documents").GetFiles "*.txt"
let counted =
files |>
Array.map (fun f -> countSpace (f.FullName) (int f.Length))
One would expect, at the end of this program, an array of integers indicating the number of spaces in each file. So how much work is it to create a concurrent version?
Creating a concurrent version using asynchronous expressions means creating an expression from the function by wrapping it in braces preceded by the identifier async (see Figure 2 ). This expression is now of the type Async<int>, and that is the type aCountSpace returns when applied to its two arguments. The only other substantial difference from the synchronous version is that the stream calls ReadAsync instead of just read. This method is made available via the Microsoft.FSharp.Control.CommonExtensions namespace.
Figure 2 Counting Spaces Asynchronously
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
}
Now the function has to be applied to arguments to obtain the expression, and the expression must be executed. That is the real difference in obtaining the final array of counts. Like the calculation of the counted array, the Array.map function applies aCountSpace to each of the files. However, applying the function only returns an asynchronous computation (because aCountSpace returns an Async<int>) that can be executed when needed. The resulting array of asynchronous computations, then, is sent to the Async.Parallel primitive, which makes the array of asynchronous computations into an asynchronous computation which returns an array of results. The expression is then passed to the Async.Run function:
let aCounted =
files
|> Array.map (fun f -> aCountSpace (f.FullName) (int f.Length))
|> Async.Parallel
|> Async.Run
Async.Run performs the final magic of running the asynchronous computation and returning the result.
Using let! and return!
You may have noticed on the other side of the stream.ReadAsync call there was an exclamation point. That is no accident. The typical let binding creates a name for a value. Asynchronous expressions offer a new technique for binding to the results of other asynchronous expressions. This approach is used to create asynchronous computations that can be composed from other expressions. As I will illustrate later, that is the key value to asynchronous expressions.
You can think of an asynchronous expression in terms of the value that is delivered. This code can be thought of and treated as the integer 1:
async { return 1}
However, you run into a little trouble when you want to deal with the value that an asynchronous expressions yields, as in the following example:
let x = async {return 1}
let y = async { let res = x
return 5 + res } // TYPE ERROR
Special consideration has to be given when performing composition of expressions. So you alter the operator slightly as a sign that you want to run this asynchronous expression at the time that the current expression is being run. You don't want to create an expression x; rather, you want to run the expression asynchronously and bind x to its resulting value. The type of res should be int, not Async<int> as in the previous examples.
The operator used to pierce the expression and access the underlying type is let!. Of course, the expression won't actually be executed until Async.Run is applied to the entire composition:
let x = async { return 1}
let y = async { let! res = x
return 5 + res } // WORKS!
Async.Run y
This results, of course, in the integer 6.
Similar to let! is return!, which is used to compose another expression directly into the return statement:
let x = async { return 1}
let y = async { return! x}
Not a very useful example yet, but it illustrates the point. When you are dealing with nested expressions, let! and return! allow you to avoid wasting intellectual effort on inventing ways to bind the expressions together. Instead, you just treat the expressions as the values they return. In fact, the above could just as well have been written using let!, as in the following:
let x = async {return 1}
let y = async { let! temp = x
return temp }
This focus on compositionality is the key to the value added by this notation. It allows the programmer to focus on the basic program logic and to keep it separated from the description of the parallel nature of the computation.
Async.Run is a way to block, waiting for the asynchronous computation to complete. But the Async.Parallel example in the code shown earlier still hasn't been fully explained. It is a function that can be applied to an array of asynchronous expressions, and it returns an asynchronous expression on an array. Async.Parallel is an asynchronous primitive, a family of functions that are designed to glue the new execution logic into asynchronous expressions while taking advantage of built-in exception handling and compositionality.
The idea of primitives is to control the way an expression is executed when it is finally applied. For example, an asynchronous expression such as the one shown here won't actually run asynchronously until the execution flow logic been plugged in to run concurrently with other processes:
let one = async { return 1 }
The key to great primitives is that they manage a computation asynchronously in a reusable pattern. You can apply Async.Parallel to an array of asynchronous expressions (Async<'a> array) and it returns an asynchronous expression that returns an array of the resulting type (Async<array<'a>>). When executed, each of the expressions in the array is executed in parallel. Async.Parallel executes each member on a separate thread and handles all of the details of managing each thread's result in the corresponding element of the returned array.
Async.Parallel is a great example of a reusable execution approach for asynchronous expressions. In particular it is useful when the number of elements in the array corresponds to the number of processor cores on the machine. (Though you don't need to worry about aligning these—the thread pool will create and manage the correct number of threads and schedule the asynchronous work as appropriate.) To generate an array quickly, you can use the [| … |] generation notation in F#:
let nums = [|async {return 1 + 1};
async {return 1+ 2};
async {return 1 + 3}|]
Async.Run (Async.Parallel nums)
Another primitive you saw previously is the ReadAsync (an asynchronous version of Stream.Read) in the stream that reads in file data. That primitive (when the Microsoft.FSharp.Control.CommonExtensions namespace is open) is available on streams that read data. The function is applied to a buffer array, a starting point, and a length. It returns an Async<int> indicating the number of bytes read. Of course, all of this is only executed when an execution function such as Async.Run or Async.Spawn is applied to the entire expression.
These primitives are the building blocks of composition. And when they are structured appropriately, you can modularize and separate the control flow details of a parallel computation away from the actual program logic. The representation of the program is abstract enough that you can begin to use new functions as computational devices that execute those programs in different ways.
Both of these primitives are asynchronous expressions, just like the ones created earlier. But when Async.Run (or another execution function such as Async.Spawn) is applied to them, they will execute, handling the details of their asynchronous approach. The alignment of these computation types as asynchronous expressions creates an expressive and compositional architecture with which you can choose how to run your programs. And this is the way to build large complex systems: by gluing together programs with simple primitives.
Create Your Own Primitives
The primitives used to stitch different expressions together are already well thought out and handle most of the logic you will want to create. While powerful libraries are a sure way to add simplicity for many tasks, much value would be added by developing highly customized solutions to meet particular needs in specific cases. Asynchronous expressions deliver the opportunity to add custom primitives when the need arises. In this section I'll walk you through an example implementation of a new asynchronous primitive.
One of the things that is exceptionally difficult in most imperative languages is to short-circuit I/O requests as a result of another call's completion. Consider the C# if statement:
if ( a || b) {Console.WriteLine("A and B were true");}
else { Console.WriteLine("Either A or B was false");}
If a is true, then b is not checked and the first statement is executed. That kind of short circuit in an I/O operation, where the goal is to get the first successful result, could be really helpful if redundant data sources are being called on. In such a case, only the first data source to return a value needs to be checked. It doesn't matter if that data source is a database, flat file, Web service, or external sensor.
This is not a trivial undertaking in an imperative language. Any attempt would run the risk of getting bogged down in the concurrency issues, which is a result of failing to separate the concurrency control from the simple expression of retrieving the first data available. Issues such as exception handling would further exacerbate the complexity.
As an example, let's send a request for data to a Microsoft TerraService database. I first reference the TerraService.dll file in the WebReferences namespace:
#light
#r "TerraService.dll"
#nowarn "57"
open System
open System.Xml
open System.Xml.Serialization
open System.IO
open Microsoft.FSharp.Control
open Microsoft.FSharp.Control.CommonExtensions
open WebReferences
As mentioned, all examples use the #light compiler directive. And, since asynchronous expressions are to date an experimental feature, I skip the warning by including #nowarn "57". I will also need to open several namespaces, including the WebReferences namespace to provide access to the TerraService Web service proxy.
The next step here is to set up a couple of types using discriminated unions:
type 'a Result =
| Complete of 'a
| Failure of 'a
type 'a RetrievedValue =
| File of 'a
| Web of 'a
| Timeout
| Error
let ts = new TerraService()
The Result type provides a description of the computation's termination, and the RetrievedValue describes the source, and possibly error, of the TerraService call. I will be caching results to the file system, hence both File and Web are included as patterns in the union. Finally, I instantiate a new TerraService type.
Primitives and Supporting Functions
Most F# code is best read from the inside out and from the bottom up. In Figure 3 , the first thing to understand is the function first, which is the actual asynchronous primitive. The only job of first is to create an asynchronous primitive that executes on a list of asynchronous expressions that happen to return one of the Result types in the discriminated union shown earlier.
Figure 3 updateAndContinue
let updateAndContinue n i cont res =
lock n (fun () -> if !n = -1 then n:=i)
if (!n=i) then cont res
let aContinueFirstSuccess cont n i x =
async {
let! y=x in
do match y with
| Complete(res) -> updateAndContinue n i cont res
| _ -> ()
}
let continueIfFirst cont n i x =
Async.Spawn (aContinueFirstSuccess cont n i x)
let first (ls:Async<'a Result> List) =
Async.Primitive
(fun (cont,exn) -> let n = ref (-1) in
List.iteri (continueIfFirst cont n ) ls)
Like all asynchronous primitives, the core section is a function that takes a continuation function (cont) and an exception function (exn). Depending on what happens in the expressions computation either one could be called, but it is up to the developer to define under what circumstances each branch is taken.
The function first creates a reference to an integer with an initial value of -1. Reference cells in F# are one way to create mutable values. They can be assigned to, much like variables in C# with the := operator. Their values are accessed by preceding the name with an exclamation point (as in the updateAndContinue function in Figure 3 ).
The negative one will be a flag that the reference remains unset. Next, the list of asynchronous expressions is iterated over using the List.iteri construct and continueIfFirst is called on every expression with cont and the integer reference n. List.iteri, unlike List.iter, will apply the integer index of the element of the list being iterated, which is used to identify each executed expression.
The function continueIfFirst, just above the function first, spawns a new thread and fires the asynchronous expression aContinueFirstSuccess with the continuation function cont, the integer reference, the index in the list of the asynchronous expressions (from ls in first) and the expression to be executed.
The asynchronous expression aContinueFirstSuccess, now executing in a separate thread for each element in the original list of asynchronous expressions, executes the asynchronous expression in the current thread and checks its result. Recall that, in the implementation of the primitive, each asynchronous expression returns one of the Result types from the discriminated union bound to a generic type (represented by 'a). If the result is Complete, then the code updates and continues by calling updateAndContinue, passing it the same arguments with the exception of the original asynchronous expression. The function updateAndContinue ensures that only the first expression that updates n is able to continue. The reference cell n is updated in a thread safe manner using lock, and the value is subsequently checked to be equal to i. If the reference cell n and i are equal then this thread must have been the first to update n. The expression has been computed now, so I pass the result as res.
Ultimately, the primitive first and its few supporting functions provide the execution mechanism to spawn a list of asynchronous computations, and return the first successful result.
One of the most obvious things that can be created from this code is a timeout mechanism. After all, any time the first of a set of computations should be returned, there will almost always be a maximum time limit for which the user should have to wait.
Here are the two functions necessary to create a timeout. The first is a function called timeout that takes the value it is to return when that time (milliseconds) has passed:
let timeout i x =
async {
do System.Threading.Thread.Sleep(i:int)
return Complete(x)
}
The second one is a function called terminateInSeconds. Note that it is intended to be composed on top of an existing asynchronous expression:
let terminateInSeconds time zero expr =
let success =
async {
let! res = expr
return Complete(res)
}
first [ success; timeout (1000*time) zero]
Using first, it executes the expression it is given and returns the zero argument if the expression does not execute successfully in the allotted time.
A very simple use of the execution of a list of asynchronous computations would be to create a countdown. The countdown could be an asynchronous expression defined as follows:
let rec countdown x =
async {
do Console.WriteLine(x:int)
do System.Threading.Thread.Sleep(1000)
if x = 0 then return Complete("Done")
else
let! res =
first [ countdown (x-1);
timeout ((x-1) * 1000) "Launch"]
return Complete(res)
}
If you run this with the Async.Run primitive and pass it the argument 5, then you will get a countdown in 1-second intervals:
> Async.Run (countdown 5);;
5
4
3
2
1
0
val it : string Result = Complete "Launch"
Notice that the result was "Launch", not "Done". While the timeout fired in each case, it only terminated the asynchronous computations when it beat the next countdown in the recursive chain. If it's run using Async.Spawn, the same thing happens, but the return of the expression is printed immediately and you can execute other operations while the countdown is going on.
Extending Async Calls to the Web
I would really like to involve this kind of shortcut in my TerraService Web service call. All I/O, whether from the file system, the database, the Web, or even complex telemetry can be included in this kind of orchestration. You may find it useful to create redundant sources of data for situations where connectivity is slow or unreliable. This is often the case when deploying global software to environments with a poor connectivity infrastructure.
I wrote two asynchronous expressions, neither of which does anything asynchronous on its own. The first is aGetPlaceWeb, which takes a triple of strings indicating the city, country, and state of a location. It then retrieves the location using TerraService and saves it to a local file using savePlace:
type TerraService with
member ts.AsyncGetPlaceFacts(p) =
Async.BuildPrimitive
((fun (callback,asyncState) ->
ts.BeginGetPlaceFacts (p,callback,asyncState)),
ts.EndGetPlaceFacts)
let aGetPlaceWeb (city,state,country) =
async {
let p = new Place(City=city, State=state, Country=country)
let! facts = ts.AsyncGetPlaceFacts(p)
do savePlace facts
return Complete(Web(facts.Center ))
}
The second is aGetPlaceFile, which also takes a triple of strings indicating the location:
let aGetPlaceFile placeVals =
async {
let name = nameFromTriple placeVals
if File.Exists(name) then
let coord = coordFromFile name
return Complete(File(coord))
else return Failure(Error)
}
It calls on nameFromTriple to retrieve a file name and then checks to see whether that file exists (as it would if the Web call had already retrieved that location). So on the first call for any location aGetPlaceWeb is the only viable option for retrieving the information. After that initial visit, you can retrieve the information from aGetPlaceFile. You might suspect that aGetPlaceFile is much faster than a call to the Web, and you should take advantage of that if you can. Further, a timeout mechanism should be included so that if none of the calls returns a successful result in a reasonable amount of time, a suitable message is offered.
A list of places is created as greatPlaces. Then getWebPlaces maps the asynchronous expression aGetPlaceWeb over greatPlaces. That is, the asynchronous expression aGetPlaceWeb is executed on every element of the list greatPlaces and that returns a list of asynchronous expressions (one for each location). The same thing is done with getFilePlaces, except that it uses aGetPlaceFile:
let greatPlaces = [
("Austin","Texas","United States");
("Las Vegas","Nevada","United States");
("Asheville","North Carolina","United States");
("Arlington","Virginia","United States");
("Laguna Beach","California","United States")]
let getWebPlaces = List.map aGetPlaceWeb greatPlaces
let getFilePlaces = List.map aGetPlaceFile greatPlaces
With these places and their subsequent retrieving expressions defined, you need to mash them together to create one complete list. In each element of that list both the file retrieval and the Web retrieval expressions should be executed asynchronously. That is where the primitive first will be used.
Once these are combined using the List.combine function, each pair is then passed to the primitive first to create an asynchronous expression that computes each in parallel and returns only the first result to the finish line:
let combineAsyncFirst l1 l2 =
List.map (fun (one,two) -> first [one;two]) (List.combine l1 l2)
let combined = (combineAsyncFirst getWebPlaces getFilePlaces)
let firstPlaces = List.map (terminateInSeconds 3 Timeout) combined
let e = Async.Parallel firstPlaces
let result = Async.Run e
The list firstPlaces is this combined list with the terminateInSeconds asynchronous expression, which terminates after the allotted seconds. In this case it returns the Timeout value from the discriminated union RetrievedValue.
The opportunities composition provides are really starting to become salient in this piece of code. Creating a list of pairs of computations in which the first one to terminate successfully (whether that is because of speed or an error) is returned is not trivial in an imperative language. Adding to that a timeout, which short circuits the entire thing, would be a considerable effort. However, using asynchronous expressions along with composition simplifies creating all of the little pieces needed to make a much more complex asynchronous computation.
This next line of code shows that all of these asynchronous computations are to be executed in parallel and, as a result, you will get a list of location information from the Web or, if already available, stored on the file system:
let e = Async.Parallel firstPlaces
Figure 4 represents the computation model this asynchronous expression created. Once a framework was in place to return the first of two concurrently executing I/O processes, a timeout mechanism was possible. The exact same mechanism could then be used again to retrieve data from two redundant sources concurrently. All of this was plugged into a larger framework that executed each I/O task in parallel.
Figure 4 Asynchronous Computation Model (Click the image for a larger view)
One can certainly imagine expanding on this composition and creating large networks of logic by reusing frameworks between parallel and first mechanisms. The countdown mechanism shows how flexible this architecture is and how it can create a clock-like framework. The interoperation of parallel and first can create the pieces, and composition allows you to glue them together.
It might seem awfully greedy to call a Web service that you might very well discard the results of later, asking the Web service to do work that may well be unnecessary. However, there are a variety of circumstances where it makes perfect sense. (For further insights on this, please see the End Bracket column in this issue at msdn.microsoft.com/magazine/cc872850.aspx .) I have deployed software in regions of the world where the real complexity is an unreliable network infrastructure. That is, in fact, where the idea of this kind of asynchronous composition came to me. Often in those circumstances having reliable networked software means being able to call on information at multiple sources redundantly.
Working with Other .NET Languages
The .NET Framework has many languages that excel at different things. One of the most powerful aspects of F# is its ability to create libraries that can be called seamlessly from any other .NET-compliant language because F# is a hybrid functional and object-oriented language. Of course, this does not happen for free. An F# library still has to be carefully designed to be intuitive to developers accustomed to other languages, such as C#.
For example, to create a sensible structure for a C# programmer, I will often embed my functions into a class-style architecture that will feel intuitive to a C# developer even though the underlying code was written in a remarkably different language:
type GreatPlaces() =
let places = List<(string * string * string)>()
member self.AddPlace(city,state,country) =
places.Add( (city,state,country) )
member self.RetrievePlaces () =
let lplaces = List.of_seq places
let result = runPlaces lplaces
Array.combine (Array.of_seq places) result
|> Array.map compileResultToken
To set up a Web service call in F# you can use the command line and automatic code generation facilities. In a mature language like C# these tools are integrated into Visual Studio and will generate references seamlessly. In F#, you just take a couple of small steps to achieve the same result. First, open the .NET command-line tool and use wsdl.exe to parse the Web service wsdl and generate a proxy in C#. Second, compile that C# into a DLL that will be referenced in the project.
For example, here is the script that you would use to communicate with the TerraServer Web service discussed earlier:
> wsdl /namespace:WebService https://terraservice.net/terraservice.asmx
Microsoft ® Web Services Description Language Utility
Writing file 'c:\dev\TerraService.cs'
>csc /target:library TerraService.cs
Now you have generated the necessary library for calling on the Web service just as you would any class. These two steps are followed by a reference indicator in the F# source. In F#, references to external libraries can be added directly in the text, so you can put this in your source file before any calls to the TerraService library:
#r "TerraService.dll"
This is the well-known power of .NET, interoperating programs that allow you to solve each small problem using the language that makes the most sense for that problem.
Chance Coble, M.S., is a scientist at Ideal Innovations Inc. His interest in functional programming started with Haskell several years ago, and he has been developing F# solutions for global production solutions related to machine learning, data mining, and data integration for the past two years.