Downloading multiple log files This code succeeds in downloading log text files from multiple URLs. Can this be made more efficient?
let private downloadLogs toDirectory =

    let createFileDirectories (url : string) =
        let directory = $@&quot;{toDirectory}\{url.Substring(13, 3)}&quot;

        try
            if not (Directory.Exists(directory))
            then 
                Directory.CreateDirectory(directory) |&gt; ignore
        with
            | ex -&gt; failwith ex.Message

    let year = DateTime.Now.Year
    let month = DateTime.Now.Month.ToString(&quot;d2&quot;)
    let day = DateTime.Now.Day

    let d01LogUrlWithDate = d01LogPartialUrl + $&quot;{year}-{month}-{day}&quot;
    let d02LogUrlWithDate = d02LogPartialUrl + $&quot;{year}-{month}-{day}&quot;
    let s01LogUrlWithDate = s01LogPartialUrl + $&quot;{year}-{month}-{day}&quot;
    let s02LogUrlWithDate = s02LogPartialUrl + $&quot;{year}-{month}-{day}&quot;

    let urlWithDateList = [d01LogUrlWithDate; d02LogUrlWithDate; s01LogUrlWithDate; s02LogUrlWithDate]

    // create folder for each url file
    urlWithDateList |&gt; List.iter createFileDirectories

    let buildFinalUrlAndRequest number url =
        try
            let finalUrl =  if number = 0 then $&quot;%s{url}-1.log&quot; else $&quot;%s{url}-{number}.log&quot;
            printfn $&quot;Searching for URL: {finalUrl}&quot;
            let response = Http.AsyncRequestStream(finalUrl) |&gt; Async.RunSynchronously
            if response.StatusCode &gt;= 200 &amp;&amp; response.StatusCode &lt;= 299
            then 
                printfn $&quot;URL Ready For Download: {url}&quot;
                Ok response 
            else // this is never triggered
                printfn $&quot;Error. Status Code: {response.StatusCode}&quot;
                Error $&quot;Status Code: {response.StatusCode}&quot;
        with 
            | ex -&gt; Error $&quot;Request Error: {ex.Message}&quot;

    let buildRequestList (state : seq&lt;Result&lt;HttpResponseWithStream, string&gt;&gt;) t =
        let range = seq{0..11}
        
        let responseSeq =
            seq{
                for num in range do
                    let numberUpdated = num + 1
                    let response = buildFinalUrlAndRequest numberUpdated t
                    response
            } 
        responseSeq |&gt; Seq.filter (fun x -&gt; x |&gt; function | Ok _ -&gt; true | Error _ -&gt; false) |&gt; Seq.append state

    let getResponse urls =
        urls 
        |&gt; Seq.fold buildRequestList Seq.empty
        |&gt; Seq.choose (fun x -&gt;
            x |&gt; function 
                | Ok response -&gt; 
                    Some (async{
                        try
                            let fileName = Path.GetFileName(response.ResponseUrl.Replace(@&quot;/&quot;, @&quot;\&quot;))
                            let subDir = response.ResponseUrl.Substring(13,3)
                            let pathName = $&quot;&quot;&quot;{toDirectory}\{subDir}\{fileName}&quot;&quot;&quot;
                            use outputFile = new FileStream(pathName, FileMode.Create)
                            do! response.ResponseStream.CopyToAsync(outputFile) |&gt; Async.AwaitTask
                            printfn $&quot;File Downloaded: {pathName}&quot;
                            return pathName
                        with
                            | ex -&gt; 
                                printfn $&quot;Download error {ex.Message}&quot;
                                return &quot;&quot;
                    }) | _ -&gt; None
        )

    urlWithDateList
    |&gt; getResponse
    |&gt; Async.Parallel
    |&gt; Async.RunSynchronously |&gt; Array.toList
There appears to be more options for parallelization and async here, and I think the fold overcomplicated what you were trying to do.
#if false
#r $&quot;FSharp.Data&quot;
#endif
open System.Net
open System.IO
open FSharp.Data

let  d01LogUrlWithDate, d02LogUrlWithDate, s01LogUrlWithDate, s02LogUrlWithDate = $&quot;&quot;,&quot;&quot;,&quot;&quot;,&quot;&quot;

module Async =
    let map f x =
        async {
            let! x' = x
            return f x'
        }
    let bind f x =
        async{
            let! x' = x
            return! f x'
        }
        
let private downloadLogs toDirectory =

    let createFileDirectories (url : string) =
        let directory = @&quot;{toDirectory}\{url.Substring(13, 3)}&quot;
        
        // if it is going to blow up, let it blow up keeping the entire stack trace
        if not &lt;| Directory.Exists directory
        // I prefer strongly typed ignore, less prone to refactoring issues
        then  Directory.CreateDirectory directory |&gt; ignore&lt;DirectoryInfo&gt;

    let urlWithDateList = // less clutter in scope
        let year = DateTime.Now.Year
        let month = DateTime.Now.Month.ToString(&quot;d2&quot;)
        let day = DateTime.Now.Day
        [
            d01LogUrlWithDate
            d02LogUrlWithDate
            s01LogUrlWithDate
            s02LogUrlWithDate
        ] |&gt; List.map (fun x -&gt; x + $&quot;{year}-{month}-{day}&quot;)

    // create folder for each url file
    urlWithDateList |&gt; List.iter createFileDirectories

    let buildFinalUrlAndRequest number url =
        async {
        
            try // this area wasn't as complex, so I left the try alone
                // I'd think this should be $&quot;%s{url}-{number+1}.log&quot; or else 1 would stop on zero's output
                let finalUrl = if number = 0 then $&quot;%s{url}-1.log&quot; else $&quot;%s{url}-{number}.log&quot;
                printfn $&quot;Searching for URL: {finalUrl}&quot;
                let! response = FSharp.Data.Http.AsyncRequestStream finalUrl
                if response.StatusCode &gt;= 200 &amp;&amp; response.StatusCode &lt;= 299
                then 
                    printfn $&quot;URL Ready For Download: {url}&quot;
                    return Ok response 
                else // this is never triggered
                    printfn $&quot;Error. Status Code: {response.StatusCode}&quot;
                    return Error $&quot;Status Code: {response.StatusCode}&quot;
            with 
                | ex -&gt; return Error $&quot;Request Error: {ex.Message}&quot;
        }

    // if you are just throwing away the Result.Error options, then you don't need to use Results
    let buildRequestList t : seq&lt;Async&lt;Result&lt;HttpResponseWithStream,string&gt;&gt;&gt; =
        seq{0..11}
        |&gt; Seq.map(fun i -&gt;
            async {
                let numberUpdated = i + 1
                let! response = buildFinalUrlAndRequest numberUpdated t
                return response
            }
        )

    let getResponse urls =
        urls
        |&gt; Seq.collect buildRequestList
        |&gt; Async.Parallel
        // these two Async.map calls could be composed together
        |&gt; Async.map(Seq.choose(function | Ok x -&gt; Some x | _ -&gt; None))
        |&gt; Async.map (Seq.map (fun response -&gt;
            async{
                let fileName = Path.GetFileName(response.ResponseUrl.Replace(@&quot;/&quot;, &quot;\&quot;&quot;)) // fix syntax highlighting here on stack
                let subDir = response.ResponseUrl.Substring(13,3)
                let pathName = $@&quot;{toDirectory}\{subDir}\{fileName}&quot; // fix syntax highlighting here on Stack
                use outputFile = new FileStream(pathName, FileMode.Create)
                do! response.ResponseStream.CopyToAsync(outputFile) |&gt; Async.AwaitTask
                printfn $&quot;File Downloaded: {pathName}&quot;
                return pathName
            }
            |&gt; Async.Catch // this way, no try needed above
            |&gt; Async.map( // map the catch to option, and log the errors
                function
                | Choice1Of2 x -&gt; Some x
                | Choice2Of2 ex -&gt; printfn $&quot;Download error {ex.Message}&quot;; None
            )
        ))
        |&gt; Async.bind Async.Parallel

    urlWithDateList
    |&gt; getResponse
    |&gt; Async.RunSynchronously
    |&gt; Seq.choose id // discard empty values
    |&gt; Seq.toList