F#链式Option.bind中如何获取最后一次httpRequest的返回值?
Hey there! Let's work through this redirect-following problem in F# step by step.
First, let's clarify your goal: you want to follow up to 3 redirects (as seen in your original code) and return the final httpRequest response, not just the Location header values from each redirect.
Let's break down your attempts:
1. Original code issue
Your first snippet has request returning an Option<string> (the Location header), so the chained Option.bind calls end up returning an Option<string> instead of the final response. That's why you're not getting the result of the last httpRequest.
2. Tuple approach error
Your second attempt uses a tuple (Option<string>, HttpResponse) to track both the next location and current response, but the error on snd happens because Option.bind expects a function that returns an Option<'T>—but snd returns a raw HttpResponse, not wrapped in an Option. This breaks the type contract for Option.bind.
3. Mutable variables: Are they okay?
Mutable variables do work in F#, but they're not the idiomatic approach. F# favors immutable, functional patterns to avoid side effects and make code more predictable. That said, if you really wanted to use mutability, you could fix your snippet (we'll get to that), but let's start with the idiomatic solution first.
Idiomatic F# Solution: Recursive Redirect Following
Instead of mutable state or tuple chaining, use a recursive function to track redirects and return the final response. This keeps state immutable and handles edge cases like hitting max redirects gracefully.
let getHtml location = // Recursive helper to follow redirects with a max limit let rec followRedirects maxRemaining url = let response = httpRequest (getFullUri url) match response.Headers.TryFind "Location", maxRemaining with // If there's a location left and we haven't hit max redirects, keep going | Some nextUrl, remaining when remaining > 0 -> followRedirects (remaining - 1) nextUrl // No more redirects, or max attempts reached: return the final response | _ -> Some response // Follow up to 3 redirects (matches your original code's chain length) followRedirects 3 location
This approach:
- Avoids mutable state entirely
- Stops automatically when there's no
Locationheader or we hit the max redirect limit (prevents infinite loops) - Clearly returns the final
HttpResponsewrapped in anOption
Mutable Variable Approach (If You Prefer)
If you still want to use mutability, here's a corrected version of your snippet. Note the proper initialization of the mutable variable and safer handling of empty Location values:
let getHtml location = // Initialize with an uncheckable default (avoids null issues) let mutable finalResp = Unchecked.defaultof<FSharp.Data.HttpResponse> let request url = let response = httpRequest (getFullUri url) finalResp <- response // Update mutable variable with current response response.Headers.TryFind "Location" // Follow redirects step by step, handling None cases let rec follow currentUrl = match request currentUrl with | Some nextUrl -> follow nextUrl | None -> () follow location // Return the final response if it was initialized if obj.ReferenceEquals(finalResp, Unchecked.defaultof<_>) then None else Some finalResp
While this works, it introduces a side effect (modifying finalResp inside request) which makes the code harder to reason about—especially if you ever need to parallelize or test this logic. The recursive approach is preferred in F# for these scenarios.
内容的提问来源于stack exchange,提问作者ca9163d9

