item is null since it has not yet been created from its parts. The F # compiler compiles the parameters as separate actual (IL-level) parameters, and not a single parameter of type Tuple<...> . If you look at your compiled code in ILSpy, you will see this signature (using C # syntax):
public static Tuple<string, string, string> calcRelTime(string item_0, string item_1, string item_2)
This is done for several reasons, including compatibility with other CLR languages, as well as efficiency.
Of course, the tuple itself is then built from these arguments (unless you have turned on optimization), but not immediately. If you take one step (press F11), item will get the correct non-zero value.
You can also see these parameters generated by the compiler if you go to Debug -> Windows -> Locals in Visual Studio.
As for why it returns the original list instead of the changed one, I cannot say: in my setup, everything works as expected:
> getRelativeTime [] [("x","05/01/2015","y")] val it : (string * string * string) list = [("x", "y", "17305")]
Perhaps if you share your test code, I can tell you more.
And finally, what you do can be made much simpler: you donโt need to write a recursive loop yourself, itโs already done for you in many functions of the List module, and you donโt know, you need to take a tuple and then deconstruct it with Using tFst , tSnd and tTrd , the compiler can do this for you:
let getRelativeTime lst = let calcRelTime (x, time, y) = let parsed = DateTime.Parse time let since = DateTime.Now - parsed let asStr = (floor since.TotalMinutes).ToString() (x, asStr, y) List.map calRelTime lst