Can you post a full example? The error means somewhere a sequence of string * string tuples is used instead of a tuple (no sequence). I don't see any usage of tuples here in the posted code, so maybe the error is happening somewhere else?

By on 4/14/2009 8:52 PM ()

The problem is in the input parameter of 'download'.
This is the code in the F# library being called:
#light

open System.IO
open System.Net
open Microsoft.FSharp.Control.CommonExtensions
open System.Threading
open System.Threading.Tasks;
open System.Threading.Collections
open System

let download (urls:string list) =
seq { use results = new BlockingCollection<(string * string)>()
use pagesRemain = new CountdownEvent(1)
let _ = Task.Create(fun _ ->
urls |> List.iter(fun url ->
let wc = new WebClient()
wc.DownloadStringCompleted.Add(fun args ->
if args.Error = null then
results.Add(((args.UserState :?> string), args.Result))
if pagesRemain.Decrement() then
results.CompleteAdding()
)

pagesRemain.Increment()
wc.DownloadStringAsync(new Uri(url), url)
)

if pagesRemain.Decrement() then results.CompleteAdding()
)

for result in results.GetConsumingEnumerable() do yield result
}

And the calling F# code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CrawlerTest
{
class Program
{

static void Main(string[] args)
{
string[] scores = { "1", "2"};

var fs_scores = Microsoft.FSharp.Collections.List<string>.get_uniq_Empty();
for (int i = scores.Length - 1; i >= 0; i--)
{
fs_scores = Microsoft.FSharp.Collections.List<string>.Cons(scores[i],
fs_scores);
}

Microsoft.FSharp.Core.Tuple<string, string> t;

t = Module1.download(fs_scores);
}
}
}

In order to use the attached code, you need System.Threading.dll 1.0.3058.34407 - the Task Parallel Library, CTP June 2008

Thanks!

By on 4/15/2009 12:51 AM ()

As the error message suggests, the return type of download isn't a single Tuple<string, string>, but a sequence of these.

1
foreach (Tuple<string, string> pair in Module1.download(ListModule.of_array(scores))) ...

PS: To make interoperability easier, you should avoid F#-specific types in your public API. I'd use

1
string [] -> YourPairTypeThatHasMeaningfulMemberNames seq

for download.

By on 4/15/2009 2:39 AM ()

Thank you for you reply. It really helped me!
I ended up rewriting the code to avoid usage of the FSharp namespace like that:

F# code:
open System.IO
open System.Net
open Microsoft.FSharp.Control.CommonExtensions
open System.Threading
open System.Threading.Tasks;
open System.Threading.Collections
open System

let download (arr : ResizeArray<string>) =
use results = new BlockingCollection<(string * string)>()
use pagesRemain = new CountdownEvent(1)
let dotnetresult = new System.Collections.Generic.Dictionary<string,string>()
let urls = List.of_seq(arr)

let _ = Task.Create(fun _ ->
urls |> List.iter(fun url ->
let wc = new WebClient()
wc.DownloadStringCompleted.Add(fun args ->
if args.Error = null then
results.Add(((args.UserState :?> string), args.Result))
if pagesRemain.Decrement() then
results.CompleteAdding()
)

pagesRemain.Increment()
wc.DownloadStringAsync(new Uri(url), url)
)

if pagesRemain.Decrement() then results.CompleteAdding()
)

for result in results.GetConsumingEnumerable() do dotnetresult.Add((fst result), (snd result))

dotnetresult

and the calling C# code like this:

string[] scores = { "[link:osnews.com",] "[link:cnn.com"};]
Dictionary<string,string> bc= File2.download(scores.ToList<string>());
foreach (string key in bc.Keys)
{
Console.WriteLine(bc[key]);
}

I know the code can be improved, so I am open to any tips.
Just keep in mind that I am quite new to F#.

Thanks again!

-Anton

By on 4/15/2009 5:17 AM ()

Hi Anton,

Thanks for the interesting code. It led me to take a look at using BlockingCollection as a way to meditate between a collection of async tasks and a synchronous stream of results. Here is code that abstracts the control pattern you're using above, e.g. allowing you to run any collection of async tasks and returning the results as a synchronous stream.

(Note: The code is only a sample, and hasn't been thoroughly tested. In addition there are numerous ways to adjust the sample to do useful things w.r.t. cancellation and exceptions (e.g. allow cancellation of the group of async tasks by putting them under an AsyncGroup, or throw away exceptional results, or return exceptions as part of the data. Exercise for the reader. Also in future releases of F# we will have helpers to mediate between System.Threading.Tasks and Async<T>)

This sort of design pattern comes under the general topic of "eliminating async-ness once it is introduced". We plan on adding a section on this to our planned 2nd edition of Expert F#, since it is an important topic. So if you have other interesting techniques that go under this heading please let us know.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 

#r @"C:\fsharp\staging\tools\ParallelExtensionsJuneCTP\System.Threading.dll"

open System.IO
open System.Net
open System.Threading
open System.Threading.Tasks;
open System.Threading.Collections
open System

let ParallelReturnAsSeq (tasks: seq<Async<'T>>) : seq<'T> =
  seq { use results = new BlockingCollection<'T>()
        use remaining = new CountdownEvent(1)
        for t in tasks do 
            remaining.Increment()
            Async.Start (async { let! res = t
                                 results.Add(res)
                                 if remaining.Decrement() then results.CompleteAdding() })
    
        if remaining.Decrement() then results.CompleteAdding()
        yield! results.GetConsumingEnumerable() }


ParallelReturnAsSeq [ for x in 1 .. 10 -> async { return x } ] |> Seq.to_list

By on 4/16/2009 9:17 AM ()

Hi,

Thanks. I will try to test your post.

I 've just made a Google code project and added some samples that I made to work:

[link:code.google.com]

Everyone is welcomle to contribute!

Cheers,

Anton

By on 4/16/2009 11:39 AM ()

I 've just made a Google code project and added some samples that I made to work:

[link:code.google.com]

Everyone is welcomle to contribute!

[link:www.codeplex.com] already exists. Maybe it would be useful to centralize everything in one place? (just a suggestion! nice initiative either way)

By on 4/16/2009 2:02 PM ()

I know.
I wrote to the owner of fsharpsample, but there was no response, so I got eager and created my own one.

-Anton

By on 4/16/2009 2:45 PM ()
IntelliFactory Offices Copyright (c) 2011-2012 IntelliFactory. All rights reserved.
Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us | Terms of Use | Privacy Policy | Cookie Policy
Built with WebSharper