Asynchronous C# and F# (I.): Simultaneous introduction
One of the exciting new technologies that was announced at PDC 2010 is the support for asynchronous programming in C#. So what exactly is asynchronous programming? Many applications today need to connect to some service or external data source including, for example, Web Services and REST APIs. These calls may take a long time to run, but we want to run them without blocking the application.
Currently, you can run the operation on a background thread or using a Task
, but
coordinating multiple such operations is difficult. What if you for example need to wait until
any (or all) of downloads complete and run some more code then? This is not only difficult, but
it also scales badly, because blocking .NET threads is a bad practice (Threads are expensive and when
they're just waiting for other operation to complete, we're wasting valuable resources).
This problem has been the main motivation for including asynchronous workflows in F#
about 3 years ago. In F#, this also enabled various interesting programming styles - for example
creating
GUI using asynchronous workflows (also discussed in Chapter 16 of my book and in
in my recent talk). The C# asynchronous
programming support and the await
keyword is largely inspired by
F# asynchronous workflows (I was quite surprised that F# wasn't more visibly
mentioned in the PDC talk).
In this article series, I'll demonstrate both F# and C# asynchronous programming model, I'll look at features that are missing in one or the other as well as a few subtle differences (that may be unexpected) and finally, I'll write about writing (asynchronous) F# libraries that are easy to use from C#. The plan for the series is following:
- Asynchronous C# and F# (I.): Simultaneous introduction
- Asynchronous C# and F# (II.): How do they differ?
- Asynchronous C# and F# (III.): How does it work?
- Asynchronous C# and F# (IV.): Calling F# libraries from C# (not yet available)
Let's start with a brief overview of asynchronous programming features in C# and F#...
Published: Friday, 29 October 2010, 4:34 AM
Tags:
c#, asynchronous, f#
Read the complete article
F# Parallel Extras (III.): Financial dashboard with cancellation
In this article we'll look at several improvements that can be done to the Financial dashboard example
(originally from the Parallel Programming with Microsoft .NET book).
When I was translating samples in the book from C# to F#, the sample struck me, because it looks like
a perfect fit for F# asynchronous workflows (instead of the Task<T>
type used in the
C# version). I already talked about an alternative
implementation based on asynchronous workflows. However that version was still following some of the
programming patterns, from the original C# version, which are not idiomatic in F#. In this article, I'll talk
about a few other improvements that we can make to the sample...
In the original version of the sample (in both C# and F#), we explicitly checked whether a cancellation
token has been cancelled in every single operation. This was needed in C#, because tasks do not support
cancellation automatically. However, F# asynchronous workflows make cancellation easier. They automatically
check if the computation should be cancelled at the beginning and the end of every asynchronous call. Our
first change will be to use this feature. Also, the original version propagates a null
value
when the computation is cancelling. In F# we don't need that and we'll only change the type of the overall
result to option<T>
, so that we can notify the user interface (but we don't need to
propagate cancellation explicitly). Finally, the original version contained sequential implementation, but
didn't provide any way of actually running it, so we'll do a bit of refactoring to make that sample
actually useable.
Published: Wednesday, 27 October 2010, 11:13 AM
Tags:
functional, parallel, asynchronous, f#
Read the complete article
F# Parallel Extras (II.): Agent-based blocking queue
In the previous article, we
briefly introduced the BlockingQueueAgent<T>
type and we used it to implement the
pipeline pattern (from Chapter 7 of Parallel Programming with Microsoft .NET)
using asynchronous workflows. The type was used to represent intermediate buffers with a limited size.
In this article we'll take a look at the implementation of the type. The type implements a very useful
pattern in agent-based parallel programming, so you can use it in your work, but it could be also
interesting as a demonstration of the F# Agent<T>
type (an recommended alias
for the MailboxProcessor<T>
type).
The BlockingQueueAgent<T>
type is similar to BlockingCollection<T>
from .NET 4.0. It has methods for adding and removing elements that block when the operation cannot be
done (e.g. adding when the queue is full or removing when the queue is empty). The most important
difference is that it can be used asynchronously. This means that when we call its operations
form F# asynchronous workflow (using let!
and do!
), the operation will block
the calling workflow, but it will not block any physical thread. We start by looking at the overall
structure of the agent and then look at the body of the agent which implements its behavior (using
a state machine)...
Published: Wednesday, 27 October 2010, 11:12 AM
Tags:
functional, parallel, asynchronous, f#
Read the complete article
F# Parallel Extras (I.): Image pipeline using agents
In a recent blog post series, I wrote about parallel programming samples that accompany the Parallel Programming with Microsoft .NET book by patterns & practices group at Microsoft. The F# translation of the samples that I wrote about mostly followed the style used in the book, so it used patterns that are typical for C#. However, some of the samples can be written in F# in a more interesting way...
In this article, we'll take a look at agent-based implementation of the Image pipeline
example (from chapter 7). A pipeline is a useful pattern if you need to process large
number of inputs in parallel and the processing consists of multiple phases or steps. In the
original implementation, the pipeline was implemented using BlockingCollection<T>
and Task<T>
types from .NET 4.0.
In this article, I'll show a
version that uses F# agents and asynchronous workflows. We'll use a BlockingQueueAgent<T>
type, which is discussed in another
article. It represents a queue with limited capacity that asynchronously blocks the process that is adding
values if there is no space in the buffer and blocks the process that reads values when there are no
values. This type can be elegantly used to implement the pipeline pattern. In this article, we'll
demonstrate it by writing a four-phase pipeline that processes images. As you'll see,
the agent-based version of the code is very much easier to write and has similar performance as
the original version.
Published: Wednesday, 27 October 2010, 11:11 AM
Tags:
functional, parallel, asynchronous, f#
Read the complete article
Reactive, parallel and concurrent programming in F# (PADL 2011)
Don Syme blogged about a paper on the F# Asynchrounous Programming Model that I helped to write. Without any doubt, the asynchronous programming features of F# are one of the reason for its success and also influence other teams in Microsoft. However, I'm very glad that there is now also an academic paper that makes this idea accessible to the academic community. I believe that the ideas could evolve in interesting ways when used in other programming languages and also, it is now easier to create research projects that build on top of the F# model.
Don already mentioned that we have another paper accepted at PADL. The paper describes work that started during my internship at Microsoft Research in 2009. It presents a simple language extension for computation expressions that makes them even more useful in some reactive, concurrent and parallel programming models. Note that this is only a research project and there are currently no plans to support the extension in the F# language (although, if there will, eventually, be an open-source F# release, then you'll hear about the extension again...)
Here is the abstract of the paper (accepted at PADL 2011) and a PDF download link:
Joinads: A retargetable control-flow construct for reactive, parallel and concurrent programming
Modern challenges led to a design of a wide range of programming models for reactive, parallel and concurrent programming, but these are often difficult to encode in general purpose languages. We present an abstract type of computations called joinads together with a syntactic language extension that aims to make it easier to use joinads in modern functional languages.
Our extension generalizes pattern matching to work on abstract computations. It keeps a familiar syntax and semantics of pattern matching making it easy to reason about code, even in a non-standard programming model. We demonstrate our extension using three important programming models – a reactive model based on events; a concurrent model based on join calculus and a parallel model using futures. All three models are implemented as libraries that benefit from our syntactic extension. This makes them easier to use and also opens space for exploring new useful programming models.
- Download the full text (PDF, pre-publication draft)
The paper can still be revised before the final publication, so any comments and suggestions for improvement are largely welcome. You can contact me either via comments (below) or using email at tomas@tomasp.net. I would be also quite interested to hear from anybody who would like to implement similar feature in other programming languages (for example Haskell or Scala).
Published: Monday, 25 October 2010, 12:00 AM
Tags:
f#, research, links, joinads
Read the complete article
Looking under the cover (How does it work?)
Languages with type-inference make it possible to write shorter code, because a smart compiler infers the types automatically. Even though we don't write the types, they are still there and are used to check whether our code doesn't contain (certain type of) bugs. However, types are not only useful for safety, but also when understanding code. When you see a function together with its type (especially higher-order functions) you can understand code much more easily than when you just see its name. When writing F# code, you can see all the types thanks to great integration for Visual Studio. However, what if you post a code sample to a web site or a blog? Many F# code snippets (including some on this blog) are just difficult to read without types.
A few weeks ago, I was talking with James Margetson (from the F# team) about the possible uses of some functionality provided by the F# compiler and he suggested that we could use them to solve the problem I described in the introduction. Wouldn't it be nice if you could place a mouse cursor over an identifier in an F# blog post and see the same type information as the one that would appear in Visual Studio?
Here is an example of what I'm talking about:
1: /// Asynchronously download the content of a web page 2: let downloadUrl url = (...) 3: 4: /// Download specified pages and add their lengths 5: let getTotalLength urls = async { 6: let! texts = 7: urls |> List.map downloadUrl 8: |> Async.Parallel 9: return texts |> Seq.sumBy String.length }F# Web Snippets
If you cannot wait and want to create F# source code snippets like this yourself, then go straight to F# Web Snippets (please use the web based version carefully - processing is pretty demanding!). If you first want to know how it works and how to use it, as well as where to get the source code, then continue reading...
from Microsoft.FSharp.Control
Full name: Untitled.downloadUrl
Asynchronously download the content of a web page
type: string
implements: IComparable
implements: ICloneable
implements: IConvertible
implements: IComparable<string>
implements: seq<char>
implements: Collections.IEnumerable
implements: IEquatable<string>
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.async
type: WebRequest
implements: Runtime.Serialization.ISerializable
inherits: MarshalByRefObject
class
inherit System.Net.WebRequest
member Abort : unit -> unit
member Accept : string with get, set
member AddRange : int -> unit
member AddRange : int * int -> unit
member AddRange : string * int -> unit
member AddRange : string * int * int -> unit
member Address : System.Uri
member AllowAutoRedirect : bool with get, set
member AllowWriteStreamBuffering : bool with get, set
member AutomaticDecompression : System.Net.DecompressionMethods with get, set
member BeginGetRequestStream : System.AsyncCallback * obj -> System.IAsyncResult
member BeginGetResponse : System.AsyncCallback * obj -> System.IAsyncResult
member ClientCertificates : System.Security.Cryptography.X509Certificates.X509CertificateCollection with get, set
member Connection : string with get, set
member ConnectionGroupName : string with get, set
member ContentLength : int64 with get, set
member ContentType : string with get, set
member ContinueDelegate : System.Net.HttpContinueDelegate with get, set
member CookieContainer : System.Net.CookieContainer with get, set
member Credentials : System.Net.ICredentials with get, set
member EndGetRequestStream : System.IAsyncResult -> System.IO.Stream
member EndGetRequestStream : System.IAsyncResult * System.Net.TransportContext -> System.IO.Stream
member EndGetResponse : System.IAsyncResult -> System.Net.WebResponse
member Expect : string with get, set
member GetRequestStream : unit -> System.IO.Stream
member GetRequestStream : System.Net.TransportContext -> System.IO.Stream
member GetResponse : unit -> System.Net.WebResponse
member HaveResponse : bool
member Headers : System.Net.WebHeaderCollection with get, set
member IfModifiedSince : System.DateTime with get, set
member KeepAlive : bool with get, set
member MaximumAutomaticRedirections : int with get, set
member MaximumResponseHeadersLength : int with get, set
member MediaType : string with get, set
member Method : string with get, set
member Pipelined : bool with get, set
member PreAuthenticate : bool with get, set
member ProtocolVersion : System.Version with get, set
member Proxy : System.Net.IWebProxy with get, set
member ReadWriteTimeout : int with get, set
member Referer : string with get, set
member RequestUri : System.Uri
member SendChunked : bool with get, set
member ServicePoint : System.Net.ServicePoint
member Timeout : int with get, set
member TransferEncoding : string with get, set
member UnsafeAuthenticatedConnectionSharing : bool with get, set
member UseDefaultCredentials : bool with get, set
member UserAgent : string with get, set
static member DefaultCachePolicy : System.Net.Cache.RequestCachePolicy with get, set
static member DefaultMaximumErrorResponseLength : int with get, set
static member DefaultMaximumResponseHeadersLength : int with get, set
end
Full name: System.Net.HttpWebRequest
type: HttpWebRequest
implements: Runtime.Serialization.ISerializable
inherits: WebRequest
inherits: MarshalByRefObject
WebRequest.Create(requestUri: Uri) : WebRequest
WebRequest.Create(requestUriString: string) : WebRequest
class
new : string -> System.Uri
new : string * bool -> System.Uri
new : string * System.UriKind -> System.Uri
new : System.Uri * string -> System.Uri
new : System.Uri * string * bool -> System.Uri
new : System.Uri * System.Uri -> System.Uri
member AbsolutePath : string
member AbsoluteUri : string
member Authority : string
member DnsSafeHost : string
member Equals : obj -> bool
member Fragment : string
member GetComponents : System.UriComponents * System.UriFormat -> string
member GetHashCode : unit -> int
member GetLeftPart : System.UriPartial -> string
member Host : string
member HostNameType : System.UriHostNameType
member IsAbsoluteUri : bool
member IsBaseOf : System.Uri -> bool
member IsDefaultPort : bool
member IsFile : bool
member IsLoopback : bool
member IsUnc : bool
member IsWellFormedOriginalString : unit -> bool
member LocalPath : string
member MakeRelative : System.Uri -> string
member MakeRelativeUri : System.Uri -> System.Uri
member OriginalString : string
member PathAndQuery : string
member Port : int
member Query : string
member Scheme : string
member Segments : string []
member ToString : unit -> string
member UserEscaped : bool
member UserInfo : string
static val UriSchemeFile : string
static val UriSchemeFtp : string
static val UriSchemeGopher : string
static val UriSchemeHttp : string
static val UriSchemeHttps : string
static val UriSchemeMailto : string
static val UriSchemeNews : string
static val UriSchemeNntp : string
static val UriSchemeNetTcp : string
static val UriSchemeNetPipe : string
static val SchemeDelimiter : string
static member CheckHostName : string -> System.UriHostNameType
static member CheckSchemeName : string -> bool
static member Compare : System.Uri * System.Uri * System.UriComponents * System.UriFormat * System.StringComparison -> int
static member EscapeDataString : string -> string
static member EscapeUriString : string -> string
static member FromHex : char -> int
static member HexEscape : char -> string
static member HexUnescape : string * int -> char
static member IsHexDigit : char -> bool
static member IsHexEncoding : string * int -> bool
static member IsWellFormedUriString : string * System.UriKind -> bool
static member TryCreate : string * System.UriKind * System.Uri -> bool
static member TryCreate : System.Uri * string * System.Uri -> bool
static member TryCreate : System.Uri * System.Uri * System.Uri -> bool
static member UnescapeDataString : string -> string
end
Full name: System.Uri
type: Uri
implements: Runtime.Serialization.ISerializable
type: WebResponse
implements: Runtime.Serialization.ISerializable
implements: IDisposable
inherits: MarshalByRefObject
type: Stream
implements: IDisposable
inherits: MarshalByRefObject
type: StreamReader
implements: IDisposable
inherits: TextReader
inherits: MarshalByRefObject
class
inherit System.IO.TextReader
new : System.IO.Stream -> System.IO.StreamReader
new : System.IO.Stream * bool -> System.IO.StreamReader
new : System.IO.Stream * System.Text.Encoding -> System.IO.StreamReader
new : System.IO.Stream * System.Text.Encoding * bool -> System.IO.StreamReader
new : System.IO.Stream * System.Text.Encoding * bool * int -> System.IO.StreamReader
new : string -> System.IO.StreamReader
new : string * bool -> System.IO.StreamReader
new : string * System.Text.Encoding -> System.IO.StreamReader
new : string * System.Text.Encoding * bool -> System.IO.StreamReader
new : string * System.Text.Encoding * bool * int -> System.IO.StreamReader
member BaseStream : System.IO.Stream
member Close : unit -> unit
member CurrentEncoding : System.Text.Encoding
member DiscardBufferedData : unit -> unit
member EndOfStream : bool
member Peek : unit -> int
member Read : unit -> int
member Read : char [] * int * int -> int
member ReadLine : unit -> string
member ReadToEnd : unit -> string
static val Null : System.IO.StreamReader
end
Full name: System.IO.StreamReader
type: StreamReader
implements: IDisposable
inherits: TextReader
inherits: MarshalByRefObject
Full name: Untitled.getTotalLength
Download specified pages and add their lengths
type: string list
implements: Collections.IStructuralEquatable
implements: IComparable<List<string>>
implements: IComparable
implements: Collections.IStructuralComparable
implements: Collections.Generic.IEnumerable<string>
implements: Collections.IEnumerable
type: string []
implements: ICloneable
implements: Collections.IList
implements: Collections.ICollection
implements: Collections.Generic.IList<string>
implements: Collections.Generic.ICollection<string>
implements: seq<string>
implements: Collections.IEnumerable
inherits: Array
module List
from Microsoft.FSharp.Collections
--------------------
type List<'T> =
| ( [] )
| ( :: ) of 'T * 'T list
with
interface Collections.IEnumerable
interface Collections.Generic.IEnumerable<'T>
member Head : 'T
member IsEmpty : bool
member Item : index:int -> 'T with get
member Length : int
member Tail : 'T list
static member Cons : head:'T * tail:'T list -> 'T list
static member Empty : 'T list
end
Full name: Microsoft.FSharp.Collections.List<_>
type: List<'T>
implements: Collections.IStructuralEquatable
implements: IComparable<List<'T>>
implements: IComparable
implements: Collections.IStructuralComparable
implements: Collections.Generic.IEnumerable<'T>
implements: Collections.IEnumerable
Full name: Microsoft.FSharp.Collections.List.map
type Async<'T>
Full name: Microsoft.FSharp.Control.Async<_>
--------------------
type Async
with
static member AsBeginEnd : computation:('Arg -> Async<'T>) -> ('Arg * AsyncCallback * obj -> IAsyncResult) * (IAsyncResult -> 'T) * (IAsyncResult -> unit)
static member AwaitEvent : event:IEvent<'Del,'T> * ?cancelAction:(unit -> unit) -> Async<'T> (requires delegate and 'Del :> Delegate)
static member AwaitIAsyncResult : iar:IAsyncResult * ?millisecondsTimeout:int -> Async<bool>
static member AwaitWaitHandle : waitHandle:Threading.WaitHandle * ?millisecondsTimeout:int -> Async<bool>
static member CancelDefaultToken : unit -> unit
static member Catch : computation:Async<'T> -> Async<Choice<'T,exn>>
static member FromBeginEnd : beginAction:(AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg:'Arg1 * beginAction:('Arg1 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * beginAction:('Arg1 * 'Arg2 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * arg3:'Arg3 * beginAction:('Arg1 * 'Arg2 * 'Arg3 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromContinuations : callback:(('T -> unit) * (exn -> unit) * (OperationCanceledException -> unit) -> unit) -> Async<'T>
static member Ignore : computation:Async<'T> -> Async<unit>
static member OnCancel : interruption:(unit -> unit) -> Async<IDisposable>
static member Parallel : computations:seq<Async<'T>> -> Async<'T []>
static member RunSynchronously : computation:Async<'T> * ?timeout:int * ?cancellationToken:Threading.CancellationToken -> 'T
static member Sleep : millisecondsDueTime:int -> Async<unit>
static member Start : computation:Async<unit> * ?cancellationToken:Threading.CancellationToken -> unit
static member StartChild : computation:Async<'T> * ?millisecondsTimeout:int -> Async<Async<'T>>
static member StartImmediate : computation:Async<unit> * ?cancellationToken:Threading.CancellationToken -> unit
static member StartWithContinuations : computation:Async<'T> * continuation:('T -> unit) * exceptionContinuation:(exn -> unit) * cancellationContinuation:(OperationCanceledException -> unit) * ?cancellationToken:Threading.CancellationToken -> unit
static member SwitchToContext : syncContext:Threading.SynchronizationContext -> Async<unit>
static member SwitchToNewThread : unit -> Async<unit>
static member SwitchToThreadPool : unit -> Async<unit>
static member TryCancelled : computation:Async<'T> * compensation:(OperationCanceledException -> unit) -> Async<'T>
static member CancellationToken : Async<Threading.CancellationToken>
static member DefaultCancellationToken : Threading.CancellationToken
end
Full name: Microsoft.FSharp.Control.Async
from Microsoft.FSharp.Collections
Full name: Microsoft.FSharp.Collections.Seq.sumBy
class
new : char -> string
new : char * int * int -> string
new : System.SByte -> string
new : System.SByte * int * int -> string
new : System.SByte * int * int * System.Text.Encoding -> string
new : char [] * int * int -> string
new : char [] -> string
new : char * int -> string
member Chars : int -> char
member Clone : unit -> obj
member CompareTo : obj -> int
member CompareTo : string -> int
member Contains : string -> bool
member CopyTo : int * char [] * int * int -> unit
member EndsWith : string -> bool
member EndsWith : string * System.StringComparison -> bool
member EndsWith : string * bool * System.Globalization.CultureInfo -> bool
member Equals : obj -> bool
member Equals : string -> bool
member Equals : string * System.StringComparison -> bool
member GetEnumerator : unit -> System.CharEnumerator
member GetHashCode : unit -> int
member GetTypeCode : unit -> System.TypeCode
member IndexOf : char -> int
member IndexOf : string -> int
member IndexOf : char * int -> int
member IndexOf : string * int -> int
member IndexOf : string * System.StringComparison -> int
member IndexOf : char * int * int -> int
member IndexOf : string * int * int -> int
member IndexOf : string * int * System.StringComparison -> int
member IndexOf : string * int * int * System.StringComparison -> int
member IndexOfAny : char [] -> int
member IndexOfAny : char [] * int -> int
member IndexOfAny : char [] * int * int -> int
member Insert : int * string -> string
member IsNormalized : unit -> bool
member IsNormalized : System.Text.NormalizationForm -> bool
member LastIndexOf : char -> int
member LastIndexOf : string -> int
member LastIndexOf : char * int -> int
member LastIndexOf : string * int -> int
member LastIndexOf : string * System.StringComparison -> int
member LastIndexOf : char * int * int -> int
member LastIndexOf : string * int * int -> int
member LastIndexOf : string * int * System.StringComparison -> int
member LastIndexOf : string * int * int * System.StringComparison -> int
member LastIndexOfAny : char [] -> int
member LastIndexOfAny : char [] * int -> int
member LastIndexOfAny : char [] * int * int -> int
member Length : int
member Normalize : unit -> string
member Normalize : System.Text.NormalizationForm -> string
member PadLeft : int -> string
member PadLeft : int * char -> string
member PadRight : int -> string
member PadRight : int * char -> string
member Remove : int -> string
member Remove : int * int -> string
member Replace : char * char -> string
member Replace : string * string -> string
member Split : char [] -> string []
member Split : char [] * int -> string []
member Split : char [] * System.StringSplitOptions -> string []
member Split : string [] * System.StringSplitOptions -> string []
member Split : char [] * int * System.StringSplitOptions -> string []
member Split : string [] * int * System.StringSplitOptions -> string []
member StartsWith : string -> bool
member StartsWith : string * System.StringComparison -> bool
member StartsWith : string * bool * System.Globalization.CultureInfo -> bool
member Substring : int -> string
member Substring : int * int -> string
member ToCharArray : unit -> char []
member ToCharArray : int * int -> char []
member ToLower : unit -> string
member ToLower : System.Globalization.CultureInfo -> string
member ToLowerInvariant : unit -> string
member ToString : unit -> string
member ToString : System.IFormatProvider -> string
member ToUpper : unit -> string
member ToUpper : System.Globalization.CultureInfo -> string
member ToUpperInvariant : unit -> string
member Trim : unit -> string
member Trim : char [] -> string
member TrimEnd : char [] -> string
member TrimStart : char [] -> string
static val Empty : string
static member Compare : string * string -> int
static member Compare : string * string * bool -> int
static member Compare : string * string * System.StringComparison -> int
static member Compare : string * string * System.Globalization.CultureInfo * System.Globalization.CompareOptions -> int
static member Compare : string * string * bool * System.Globalization.CultureInfo -> int
static member Compare : string * int * string * int * int -> int
static member Compare : string * int * string * int * int * bool -> int
static member Compare : string * int * string * int * int * System.StringComparison -> int
static member Compare : string * int * string * int * int * System.Globalization.CultureInfo * System.Globalization.CompareOptions -> int
static member Compare : string * int * string * int * int * bool * System.Globalization.CultureInfo -> int
static member CompareOrdinal : string * string -> int
static member CompareOrdinal : string * int * string * int * int -> int
static member Concat : obj -> string
static member Concat : obj [] -> string
static member Concat : string [] -> string
static member Concat : obj * obj -> string
static member Concat : string * string -> string
static member Concat : obj * obj * obj -> string
static member Concat : string * string * string -> string
static member Concat : obj * obj * obj * obj -> string
static member Concat : string * string * string * string -> string
static member Copy : string -> string
static member Equals : string * string -> bool
static member Equals : string * string * System.StringComparison -> bool
static member Format : string * obj -> string
static member Format : string * obj [] -> string
static member Format : string * obj * obj -> string
static member Format : System.IFormatProvider * string * obj [] -> string
static member Format : string * obj * obj * obj -> string
static member Intern : string -> string
static member IsInterned : string -> string
static member IsNullOrEmpty : string -> bool
static member Join : string * string [] -> string
static member Join : string * string [] * int * int -> string
end
Full name: System.String
type: String
implements: IComparable
implements: ICloneable
implements: IConvertible
implements: IComparable<string>
implements: seq<char>
implements: Collections.IEnumerable
implements: IEquatable<string>
Full name: Microsoft.FSharp.Core.String.length
let req = HttpWebRequest.Create(Uri(url))
let! resp = req.AsyncGetResponse()
let stream = resp.GetResponseStream()
let reader = new StreamReader(stream)
return! reader.AsyncReadToEnd() }
Published: Monday, 18 October 2010, 1:42 AM
Tags:
functional, f#
Read the complete article