Reputation: 2891
I am new to programming and F# is my first language.
Here are the relevant parts of my code:
open System.IO
open System.Net
let downloadHtmlFromUrlAsync (url: string) =
async {
let uri = new System.Uri(url)
let webClient = new WebClient()
let! html = webClient.AsyncDownloadString(uri)
return html
}
let downloadHtmlToDisk (url: string) (directoryPath: string) =
if isValidUrl url then
let name = getNameFromRedirectedUrl url
let id = getIdFromUrl url
let html = downloadHtmlFromUrlAsync url
let newTextFile = File.Create(directoryPath + "\\" + id.ToString("00000") + " " + name.TrimEnd([|' '|]) + ".html")
use file = new StreamWriter(newTextFile)
file.Write(html)
file.Close()
let downloadEntireDatabase (baseUrl: string) (totalNumberOfPeople: int) =
let allIds = [ for i in 1 .. totalNumberOfPeople -> i ]
allIds
|> Seq.map (fun id -> baseUrl + string(id))
|> Seq.filter isValidUrl
|> Seq.map downloadHtmlToDisk
|> Async.Parallel
|> Async.RunSynchronously
I have tested the functions isValidUrl, getNameFromRedirectedUrl, getIdFromUrl in F# interactive. They work fine.
My problem is this: When I try to run the code pasted above, the following error message is produced:
Program.fs(483,8): error FS0193: Type constraint mismatch. The type
seq<(string -> unit)>
is not compatible with typeseq<Async<'a>>
The typeAsync<'a>
does not match the typestring -> unit
What went wrong? What changes should I make?
Upvotes: 2
Views: 141
Reputation: 52280
The problem is probably this line (can you please give us the definition of downloadFighterHtmlToDisk
):
allIds
...
|> Seq.map downloadFighterHtmlToDisk
...
based on the error message this functions seems to have a signature string -> string -> unit
but you really need string -> Async<'something>
.
Now I guess you used downloadHtmlToDisk
or something similar and you can but then I would suggest rewriting it to:
let downloadHtmlToDisk (directoryPath: string) (url: string) =
async {
if isValidUrl url then
let name = getNameFromRedirectedUrl url
let id = getIdFromUrl url
let! html = downloadHtmlFromUrlAsync url
let newTextFile = File.Create(directoryPath + "\\" + id.ToString("00000") + " " + name.TrimEnd([|' '|]) + ".html")
use file = new StreamWriter(newTextFile)
file.Write(html)
}
and use it like
let downloadEntireDatabase (baseUrl: string) (totalNumberOfPeople: int) =
let allIds = [ for i in 1 .. totalNumberOfPeople -> i ]
allIds
|> Seq.map (fun id -> (id, baseUrl + string(id)))
|> Seq.filter (fun (_,url) -> isValidUrl url)
|> Seq.map (fun (id,url) -> downloadHtmlToDisk (getFighterPath id) url)
|> Async.Parallel
|> Async.RunSynchronously
See the let! html = ..
? This is important - this is where the async
will happen ;) - if you want you can find similar operations to write your file asynchronously. Also you don't need to close your file - dispose should handle it
I have just seen that you reextract the id from the url - you might also use this instead of the way I used tuples but I think it's better to really pass the id on if you still need it - for example in downloadHtmlToDisk
you really need the id
and could have created the url
from the id
there instead - a much easier approach IMO but I don't want to rewrite everything you go - just experiment a bit with this stuff
Upvotes: 2