Simultaneously binding a tuple and its destructured elements in Erlang - functional-programming

I'm destructuring a tuple and binding its elements to variables in an Erlang function signature, like so:
store({X, Y}, State) ->
...
But sometimes I need to bind the source tuple and its contents. So far I've handled that with an extra line of code:
store(Point, State) ->
{X, Y} = Point,
...
But I'm new to Erlang and wonder if this is naive. My question is inspired by binding in a Scala extraction:
case point#Point(x, y) => ???
Is there a better way to bind the tuple and its contents at the same time, or is it best to destructure the tuple in a separate assignment?

You can write
store({X, Y} = Point, State) ->
...
which works similarly to the Scala example.

If you use
store({X, Y}, State) ->
...
In this case, when you invoke a function like mod:store({x, y, z}, state), it will raise an exception:
error: no function clause matching mod:store{x, y, z}, state);
while if you use
store(Point, State) ->
{X, Y} = Point,
...
In this case, when you invoke a function like mod:store({x, y, z}, state), it will raise an exception:
error: no match of right hand side value {x, y, z}
in function mod:store/2;

Related

Confused about F# method signature syntax

I am supposed to write a small program in F# for a uni assignment. One of the exercises says to create a filter method with this signature:
filter : ('a -> bool) -> list<'a> -> list<'a>. But I am struggling to properly interpret this syntax. The docs say the syntax for creating a method is
let [inline] function-name parameter-list [ : return-type ] = function-body. But how does my example fit into this? Is it a function which takes no parameters but returns three values?
The function should filter a list given a predicate which is simple enough, but if it doesn't take any parameters, how should I pass a predicate and list? I am sure I'm missing something major because I can't wrap my head around it.
The documentation you may be referring to tells you how to implement a function. The signature you've been given, however, is the desired function's type. F# types are documented here: https://learn.microsoft.com/dotnet/fsharp/language-reference/fsharp-types
Specifically, the documentation says that in its simplest form, a function has the type parameter-type1 -> return-type, but when it has more parameters, it generally takes the form parameter-type1 -> parameter-type2 -> ... -> return-type.
In F#, functions are values, so the desired filter is a value that happens to be a function. The function should take two inputs: ('a -> bool) and list<'a>, and return a value of the type list<'a>.
One of the inputs is a function in its own right: ('a -> bool).
Technically, this is saying that filter is a function which takes a predicate function of type 'a -> bool and returns a function which takes a list<'a> and returns another value of type list<'a>.
This is because functions only transform one value into another, but either of those values can be a function.
As a practical matter, filter takes two arguments: that predicate function which take one 'a value and returns a boolean, and a list<'a>.
The simplest answer to your question is that that function takes two arguments and returns a value:
('a -> bool) // arg 1
-> list<'a> // arg 2
-> list<'a> // return value
In F#, function arguments can also be thought of as part of the return value because of partially applied functions e.g. you can think of the above as "given the first arg, return back a new function that expects the second arg and gives back the filtered list".
('a -> bool) // arg
-> (list<'a> -> list<'a>) // return value

Functional composition of Optionals

I have 2 Optionals (or Maybe objects) that I would like to combine so that I get the following results:
|| first operand
second ++-------------+-------------
operand || empty | optional(x)
============||=============|=============
empty || empty | optional(x)
------------++-------------+-------------
optional(y) || optional(y) |optional(x+y)
In other words, a non-empty Optional always replaces/overwrites an empty one, and two non-empty Optionals are combined according to some + function.
Initially, I assumed that the standard monadic flatMap method would do the trick, but (at least in Java) Optional.flatMap always returns an empty optional when the original Optional was already empty (and I'm not sure if any other implementation would comply with the Monad Laws).
Then, as both operands are wrapped in the same monadic type, I figured that this might be a good job for an Applicative Functor. I tried a couple different functional libraries, but I couldn't implement the desired behavior with any of the zip/ap methods that I tried.
What I'm trying to do seems to me a fairly common operation that one might do with Optionals, and I realize that I could just write my own operator with the desired behavior. Still, I am wondering if there is a standard function/method in functional programming to achieve this common operation?
Update: I removed the java tag, as I'm curious how other languages handle this situation
In a functional language, you'd do this with pattern matching, such as (Haskell):
combine :: Maybe t -> Maybe t -> (t -> t -> t) -> Maybe t
combine (Some x) (Some y) f = Some (f x y)
combine (Some x) _ _ = (Some x)
combine _ (Some y) _ = (Some y)
combine None None _ = None
There are other ways to write it, but you are basically pattern matching on the cases. Note that this still involves "unpacking" the optionals, but because its built into the language, it is less obvious.
In Haskell you can do this by wrapping any semigroup in a Maybe. Specifically, if you want to add numbers together:
Prelude> import Data.Semigroup
Prelude Data.Semigroup> Just (Sum 1) <> Just (Sum 2)
Just (Sum {getSum = 3})
Prelude Data.Semigroup> Nothing <> Just (Sum 2)
Just (Sum {getSum = 2})
Prelude Data.Semigroup> Just (Sum 1) <> Nothing
Just (Sum {getSum = 1})
Prelude Data.Semigroup> Nothing <> Nothing
Nothing
The above linked article contains more explanations, and also some C# examples.
It's not possible to combine optional objects without "unpacking" them.
I don't know the specifics of your case. For me, creating such a logic just in order to fuse the two optionals is an overkill.
But nevertheless, there's a possible solution with streams.
I assume that you're not going to pass optional objects as arguments (because such practice is discouraged). Therefore, there are two dummy methods returning Optional<T>.
Method combine() expects a BinaryOperator<T> as an argument and creates a stream by concatenating singleton-streams produced from each of the optional objects returned by getX() and getY().
The flavor of reduce(BinaryOperator) will produce an optional result.
public static <T> Optional<T> getX(Class<T> t) {
return // something
}
public static <T> Optional<T> getY(Class<T> t) {
return // something
}
public static <T> Optional<T> combine(BinaryOperator<T> combiner,
Class<T> t) {
return Stream.concat(getX(t).stream(), getY(t).stream())
.reduce(combiner);
}
If we generalize the problem to "how to combine N optional objects" then it can be solved like this:
#SafeVarargs
public static <T> Optional<T> combine(BinaryOperator<T> combiner,
Supplier<Optional<T>>... suppliers) {
return Arrays.stream(suppliers)
.map(Supplier::get) // fetching Optional<T>
.filter(Optional::isPresent) // filtering optionals that contain results to avoid NoSuchElementException while invoking `get()`
.map(Optional::get) // "unpacking" optionals
.reduce(combiner);
}
Here's one way:
a.map(x -> b.map(y -> x + y).orElse(x)).or(() -> b)
Ideone Demo
OptionalInt x = ...
OptionalInt y = ...
OptionalInt sum = IntStream.concat(x.stream(), y.stream())
.reduce(OptionalInt.empty(),
(opt, z) -> OptionalInt.of(z + opt.orElse(0)));
Since java 9 you can turn an Optional into a Stream.
With concat you get a Stream of 0, 1 or 2 elements.
Reduce it to an empty when 0 elements,and for more add it to the previous OptionalInt, defaulting to 0.
Not very straight (.sum()) because of the need for an empty().
You can implement your function in Java by combining flatMap and map:
optA.flatMap(a -> optB.map(b -> a + b));
More general example:
public static void main(String[] args) {
test(Optional.empty(), Optional.empty());
test(Optional.of(3), Optional.empty());
test(Optional.empty(), Optional.of(4));
test(Optional.of(3), Optional.of(4));
}
static void test(Optional<Integer> optX, Optional<Integer> optY) {
final Optional<Integer> optSum = apply(Integer::sum, optX, optY);
System.out.println(optX + " + " + optY + " = " + optSum);
}
static <A, B, C> Optional<C> apply(BiFunction<A, B, C> fAB, Optional<A> optA, Optional<B> optB) {
return optA.flatMap(a -> optB.map(b -> fAB.apply(a, b)));
}
Since flatMap and map are standard functions for Optional/Maybe (and monad types generally), this approach should work in any other language (though most FP languages will have a more concise solution). E.g. in Haskell:
combine ma mb = do a <- ma ; b <- mb ; return (a + b)
In F#, i would call this logic reduce.
Reason:
The function must be of type 'a -> 'a -> 'a as it only can combine thinks of equal type.
Like other reduce operations, like on list, you always need at least one value, otherwise it fails.
With a option and two of them, you just need to cover four cases. In F# it will be written this way.
(* Signature: ('a -> 'a -> 'a) -> option<'a> -> option<'a> -> option<'a> *)
let reduce fn x y =
match x,y with
| Some x, Some y -> Some (fn x y)
| Some x, None -> Some x
| None , Some y -> Some y
| None , None -> None
printfn "%A" (reduce (+) (Some 3) (Some 7)) // Some 10
printfn "%A" (reduce (+) (None) (Some 7)) // Some 7
printfn "%A" (reduce (+) (Some 3) (None)) // Some 3
printfn "%A" (reduce (+) (None) (None)) // None
In another lets say Pseudo-like C# language, it would look like.
Option<A> Reduce(Action<A,A,A> fn, Option<A> x, Option<A> y) {
if ( x.isSome ) {
if ( y.isSome ) {
return Option.Some(fn(x.Value, y.Value));
}
else {
return x;
}
}
else {
if ( y.isSome ) {
return y;
}
else {
return Option.None;
}
}
}

SML: Value restriction error when recursively calling quicksort

I'm writing a quicksort function for an exercise. I already know of the 5-line functional quicksort; but I wanted to improve the partition by having it scan through the list once and return a pair of lists splitting the original list in half. So I wrote:
fun partition nil = (nil, nil)
| partition (pivot :: rest) =
let
fun part (lst, pivot, (lesseq, greater)) =
case lst of
[] => (lesseq, greater)
| (h::t) =>
if h <= pivot then part (t, pivot, (h :: lesseq, greater))
else part (t, pivot, (lesseq, h :: greater))
in
part (rest, pivot, ([pivot], []))
end;
This partitions well enough. It gives me a signature val partition = fn : int list -> int list * int list. It runs as expected.
It's when I use the quicksort below that things start to break.
fun quicksort_2 nil = nil
| quicksort_2 lst =
let
val (lesseq, greater) = partition lst
in
quicksort_2 lesseq # quicksort_2 greater
end;
I can run the above function if I eliminate the recursive calls to quicksort_2; but if I put them back in (to actually go and sort the thing), it will cease to run. The signature will be incorrect as well, giving me val quicksort_2 = fn : int list -> 'a list. The warning I receive when I call the function on a list is:
Warning: type vars not generalized because of value restriction are instantiated to dummy types (X1,X2,...)
What is the problem here? I'm not using any ref variables; the type annotation I've tried doesn't seem to help...
The main issue is that you're lacking the singleton list base case for your quicksort function. It ought to be
fun quicksort [ ] = [ ]
| quicksort [x] = [x]
| quicksort xs =
let
val (l, r) = partition xs
in
quicksort l # quicksort r
end
which should then have type int list -> int list given the type of your partition. We have to add this case as otherwise you'll never hit a base case and instead recurse indefinitely.
For some more detail on why you saw the issues you were having though:
The signature will be incorrect as well, giving me val quicksort_2 = fn : int list -> 'a list
This is because the codomain of your function was never restricted to be less general than 'a list. Taking a look at the possible branches in your original implementation we can see that in the nil branch you return nil (of most general type 'a list) and in the recursive case you get two 'a lists (per our assumptions thus far) and append them, resulting in an 'a list---this is fine so your type is not further restricted.
[Value Restriction Warning]
What is the problem here? I'm not using any ref variables
The value restriction isn't really related to refs (though can often arise when using them). Instead it is the prohibition that anything polymorphic at the top level must be a value by its syntax (and thus precludes the possibility that a computation is behind a type abstractor at the top level). Here it is because given xs : int list we (ignoring the value restriction) have quicksort_2 xs : 'a list---which would otherwise be polymorphic, but is not a syntactic value. Correspondingly it is value restricted.

Understanding side effects with monadic traversal

I am trying to properly understand how side effects work when traversing a list in F# using monadic style, following Scott's guide here
I have an AsyncSeq of items, and a side-effecting function that can return a Result<'a,'b> (it is saving the items to disk).
I get the general idea - split the head and tail, apply the func to the head. If it returns Ok then recurse through the tail, doing the same thing. If an Error is returned at any point then short circuit and return it.
I also get why Scott's ultimate solution uses foldBack rather than fold - it keeps the output list in the same order as the input as each processed item is prepended to the previous.
I can also follow the logic:
The result from the list's last item (processed first as we are using foldback) will be passed as the accumulator to the next item.
If it is an Error and the next item is Ok, the next item is discarded.
If the next item is an Error, it replaces any previous results and becomes the accumulator.
That means by the time you have recursed over the entire list from right to left and ended up at the start, you either have an Ok of all of the results in the correct order or the most recent Error (which would have been the first to occur if we had gone left to right).
The thing that confuses me is that surely, since we are starting at the end of the list, all side effects of processing every item will take place, even if we only get back the last Error that was created?
This seems to be confirmed here as the print output starts with [5], then [4,5], then [3,4,5] etc.
The thing that confuses me is that this isn't what I see happening when I use AsyncSeq.traverseChoiceAsync from the FSharpx lib (which I wrapped to process Result instead of Choice). I see side effects happening from left to right, stopping on the first error, which is what I want to happen.
It also looks like Scott's non-tail recursive version (which doesn't use foldBack and just recurses over the list) goes from left to right? The same goes for the AsyncSeq version. That would explain why I see it short circuit on the first error but surely if it completes Ok then the output items would be reversed, which is why we normally use foldback?
I feel I am misunderstanding or misreading something obvious! Could someone please explain it to me? :)
Edit:
rmunn has given a really great comprehensive explanation of the AsyncSeq traversal below. The TLDR was that
Scott's initial implementation and the AsyncSeq traverse both do go from left to right as I thought and so only process until they hit an error
they keep their contents in order by prepending the head to the processed tail rather than prepending each processed result to the previous (which is what the built in F# fold does).
foldback would keep things in order but would indeed execute every case (which could take forever with an async seq)
It's pretty simple: traverseChoiceAsync isn't using foldBack. Yes, with foldBack the last item would be processed first, so that by the time you get to the first item and discover that its result is Error you'd have triggered the side effects of every item. Which is, I think, precisely why whoever wrote traverseChoiceAsync in FSharpx chose not to use foldBack, because they wanted to ensure that side effects would be triggered in order, and stop at the first Error (or, in the case of the Choice version of the function, the first Choice2Of2 — but I'll pretend from this point on that that function was written to use the Result type.)
Let's look at the traverseChoieAsync function in the code you linked to, and read through it step-by-step. I'll also rewrite it to use Result instead of Choice, because the two types are basically identical in function but with different names in the DU, and it'll be a little easier to tell what's going on if the DU cases are called Ok and Error instead of Choice1Of2 and Choice2Of2. Here's the original code:
let rec traverseChoiceAsync (f:'a -> Async<Choice<'b, 'e>>) (s:AsyncSeq<'a>) : Async<Choice<AsyncSeq<'b>, 'e>> = async {
let! s = s
match s with
| Nil -> return Choice1Of2 (Nil |> async.Return)
| Cons(a,tl) ->
let! b = f a
match b with
| Choice1Of2 b ->
return! traverseChoiceAsync f tl |> Async.map (Choice.mapl (fun tl -> Cons(b, tl) |> async.Return))
| Choice2Of2 e ->
return Choice2Of2 e }
And here's the original code rewritten to use Result. Note that it's a simple rename, and none of the logic needs to be changed:
let rec traverseResultAsync (f:'a -> Async<Result<'b, 'e>>) (s:AsyncSeq<'a>) : Async<Result<AsyncSeq<'b>, 'e>> = async {
let! s = s
match s with
| Nil -> return Ok (Nil |> async.Return)
| Cons(a,tl) ->
let! b = f a
match b with
| Ok b ->
return! traverseChoiceAsync f tl |> Async.map (Result.map (fun tl -> Cons(b, tl) |> async.Return))
| Error e ->
return Error e }
Now let's step through it. The whole function is wrapped inside an async { } block, so let! inside this function means "unwrap" in an async context (essentially, "await").
let! s = s
This takes the s parameter (of type AsyncSeq<'a>) and unwraps it, binding the result to a local name s that henceforth will shadow the original parameter. When you await the result of an AsyncSeq, what you get is the first element only, while the rest is still wrapped in an async that needs to be further awaited. You can see this by looking at the result of the match expression, or by looking at the definition of the AsyncSeq type:
type AsyncSeq<'T> = Async<AsyncSeqInner<'T>>
and AsyncSeqInner<'T> =
| Nil
| Cons of 'T * AsyncSeq<'T>
So when you do let! x = s when s is of type AsyncSeq<'T>, the value of x will either be Nil (when the sequence has run to its end) or it will be Cons(head, tail) where head is of type 'T and tail is of type AsyncSeq<'T>.
So after this let! s = s line, our local name s now refers to an AsyncSeqInner type, which contains the head item of the sequence (or Nil if the sequence was empty), and the rest of the sequence is still wrapped in an AsyncSeq so it has yet to be evaluated (and, crucially, its side effects have not yet happened).
match s with
| Nil -> return Ok (Nil |> async.Return)
There's a lot happening in this line, so it'll take a bit of unpacking, but the gist is that if the input sequence s had Nil as its head, i.e. had reached its end, then that's not an error, and we return an empty sequence.
Now to unpack. The outer return is in an async keyword, so it takes the Result (whose value is Ok something) and turns it into an Async<Result<something>>. Remembering that the return type of the function is declared as Async<Result<AsyncSeq>>, the inner something is clearly an AsyncSeq type. So what's going on with that Nil |> async.Return? Well, async isn't an F# keyword, it's the name of an instance of AsyncBuilder. Inside a computation expression foo { ... }, return x is translated into foo.Return(x). So calling async.Return x is just the same as writing async { return x }, except that it avoids nesting a computation expression inside another computation expression, which would be a little nasty to try and parse mentally (and I'm not 100% sure the F# compiler allows it syntactically). So Nil |> async.Return is async.Return Nil which means it produces a value of Async<x> where x is the type of the value Nil. And as we just saw, this Nil is a value of type AsyncSeqInner, so Nil |> async.Return produces an Async<AsyncSeqInner>. And another name for Async<AsyncSeqInner> is AsyncSeq. So this whole expression produces an Async<Result<AsyncSeq>> that has the meaning of "We're done here, there are no more items in the sequence, and there was no error".
Phew. Now for the next line:
| Cons(a,tl) ->
Simple: if the next item in the AsyncSeq named s was a Cons, we deconstruct it so that the actual item is now called a, and the tail (another AsyncSeq) is called tl.
let! b = f a
This calls f on the value we just got out of s, and then unwraps the Async part of f's return value, so that b is now a Result<'b, 'e>.
match b with
| Ok b ->
More shadowed names. Inside this branch of the match, b now names a value of type 'b rather than a Result<'b, 'e>.
return! traverseResultAsync f tl |> Async.map (Result.map (fun tl -> Cons(b, tl) |> async.Return))
Hoo boy. That's too much to tackle at once. Let's write this as if the |> operators were lined up on separate lines, and then we'll go through each step one at a time. (Note that I've wrapped an extra pair of parentheses around this, just to clarify that it's the final result of this whole expression that will be passed to the return! keyword).
return! (
traverseResultAsync f tl
|> Async.map (
Result.map (
fun tl -> Cons(b, tl) |> async.Return)))
I'm going to tackle this expression from the inside out. The inner line is:
fun tl -> Cons(b, tl) |> async.Return
The async.Return thing we've already seen. This is a function that takes a tail (we don't currently know, or care, what's inside that tail, except that by the necessity of the type signature of Cons it must be an AsyncSeq) and turns it into an AsyncSeq that is b followed by the tail. I.e., this is like b :: tl in a list: it sticks b onto the front of the AsyncSeq.
One step out from that innermost expression is:
Result.map
Remember that the function map can be thought of in two ways: one is "take a function and run it against whatever is "inside" this wrapper". The other is "take a function that operates on 'T and make it into a function that operates on Wrapper<'T>". (If you don't have both of those clear in your mind yet, https://sidburn.github.io/blog/2016/03/27/understanding-map is a pretty good article to help grok that concept). So what this is doing is taking a function of type AsyncSeq -> AsyncSeq and turning it into a function of type Result<AsyncSeq> -> Result<AsyncSeq>. Alternately, you could think of it as taking a Result<tail> and calling fun tail -> ... against that tail result, then re-wrapping the result of that function in a new Result. Important: Because this is using Result.map (Choice.mapl in the original) we know that if tail is an Error value (or if the Choice was a Choice2Of2 in the original), the function will not be called. So if traverseResultAsync produces a result that starts with an Error value, it's going to produce an <Async<Result<foo>>> where the value of Result<foo> is an Error, and so the value of the tail will be discarded. Keep that in mind for later.
Okay, next step out.
Async.map
Here, we have a Result<AsyncSeq> -> Result<AsyncSeq> function produced by the inner expression, and this converts it to an Async<Result<AsyncSeq>> -> Async<Result<AsyncSeq>> function. We've just talked about this, so we don't need to go over how map works again. Just remember that the effect of this Async<Result<AsyncSeq>> -> Async<Result<AsyncSeq>> function that we've built up will be the following:
Await the outer async.
If the result is Error, return that Error.
If the result is Ok tail, produce an Ok (Cons (b, tail)).
Next line:
traverseResultAsync f tl
I probably should have started with this, because this will actually run first, and then its value will be passed into the Async<Result<AsyncSeq>> -> Async<Result<AsyncSeq>> function that we've just analysed.
So what this whole thing will do is to say "Okay, we took the first part of the AsyncSeq we were handed, and passed it to f, and f produced an Ok result with a value we're calling b. So now we need to process the rest of the sequence similarly, and then, if the rest of the sequence produces an Ok result, we'll stick b on the front of it and return an Ok sequence with contents b :: tail. BUT if the rest of the sequence produces an Error, we'll throw away the value of b and just return that Error unchanged."
return!
This just takes the result we just got (either an Error or an Ok (b :: tail), already wrapped in an Async) and returns it unchanged. But note that the call to traverseResultAsync is NOT tail-recursive, because its value had to be passed into the Async.map (...) expression first.
And now we still have one more bit of traverseResultAsync to look at. Remember when I said "Keep that in mind for later"? Well, that time has arrived.
| Error e ->
return Error e }
Here we're back in the match b with expression. If b was an Error result, then no further recursive calls are made, and the whole traverseResultAsync returns an Async<Result> where the Result value is Error. And if we were currently nested deep inside a recursion (i.e., we're in the return! traverseResultAsync ... expression), then our return value will be Error, which means the result of the "outer" call, as we've kept in mind, will also be Error, discarding any other Ok results that might have happened "before".
Conclusion
And so the effect of all of that is:
Step through the AsyncSeq, calling f on each item in turn.
The first time f returns Error, stop stepping through, throw away any previous Ok results, and return that Error as the result of the whole thing.
If f never returns Error and instead returns Ok b every time, return an Ok result that contains an AsyncSeq of all those b values, in their original order.
Why are they in their original order? Because the logic in the Ok case is:
If sequence was empty, return an empty sequence.
Split into head and tail.
Get value b from f head.
Process the tail.
Stick value b in front of the result of processing the tail.
So if we started with (conceptually) [a1; a2; a3], which actually looks like Cons (a1, Cons (a2, Cons (a3, Nil))) we'll end up with Cons (b1, Cons (b2, Cons (b3, Nil))) which translates to the conceptual sequence [b1; b2; b3].
See #rmunn's great answer above for the explanation. I just wanted to post a little helper for anyone that reads this in the future, it allows you to use the AsyncSeq traverse with Results instead of the old Choice type it was written with:
let traverseResultAsyncM (mapping : 'a -> Async<Result<'b,'c>>) source =
let mapping' =
mapping
>> Async.map (function
| Ok x -> Choice1Of2 x
| Error e -> Choice2Of2 e)
AsyncSeq.traverseChoiceAsync mapping' source
|> Async.map (function
| Choice1Of2 x -> Ok x
| Choice2Of2 e -> Error e)
Also here is a version for non-async mappings:
let traverseResultM (mapping : 'a -> Result<'b,'c>) source =
let mapping' x = async {
return
mapping x
|> function
| Ok x -> Choice1Of2 x
| Error e -> Choice2Of2 e
}
AsyncSeq.traverseChoiceAsync mapping' source
|> Async.map (function
| Choice1Of2 x -> Ok x
| Choice2Of2 e -> Error e)

Why isn't this F# code tail-recursive?

I'm looking at the code in the F# 'Tutorial' template that is provided with Visual Studio 2015 and I see this code; I'm wondering why the first function isn't tail-recursive; I think I understand it but want to confirm:
/// Computes the sum of a list of integers using recursion.
let rec sumList xs =
match xs with
| [] -> 0
| y::ys -> y + sumList ys
/// Make the function tail recursive, using a helper function with a result accumulator
let rec private sumListTailRecHelper accumulator xs =
match xs with
| [] -> accumulator
| y::ys -> sumListTailRecHelper (accumulator+y) ys
Is the first one not tail recursive in the because '+' is a function and its' two arguments are evaluated first? Therefore the actual order of evaluation would be: y, then sumList ys, then +? Whereas in the second case, the order of evaluation is: accumulator,y,+ then sumListTailRecHelper(..)?
A call is tail-recursive if there's nothing left to do after the recursive call returns. So the last call amounts to going back to the start of the function code, with modified parameters.
In the first function you still have to add y to the result.

Resources