I noticed that the following code gives an error when you try to compile it:
let xx =
seq {
let! i = [ 1; 2 ]
let! j = [ 3; 4 ]
yield (i,j)
}
The error this gives is "error FS0795: The use of 'let! x = coll' in sequence expressions is no longer permitted. Use 'for x in coll' instead." This message is of course clear and demonstrates how to fix it; the fixed code would be:
let xx =
seq {
for i in [ 1; 2 ] do
for j in [ 3; 4 ] do
yield (i,j)
}
My question is not how to fix this however, but why the "let!" is not allowed in sequence expressions in the first place? I can see how the fact that let! iterates over an expression may come as a surprise to some, but that shouldn't be enough to disallow the construct. I also see how "for" is more powerful here, as the version with "let!" bakes in the scope of the iteration as "until the end of the sequence expression".
However, being able to iterate over a sequence without having to indent code was exactly what I was looking for (for traversing tree structures). I assume that to obtain this semantic I will have to make a new expression builder that acts mostly like the "seq" expression builder, but does allow "let!" for iteration, isn't it?
Added, based on Brian's comment below, providing the solution to my underlying problem:
I didn't realize the indentation in the for blocks isn't needed, and the second sample can be re-written as:
let xx =
seq {
for i in [ 1; 2 ] do
for j in [ 3; 4 ] do
yield (i,j)
}
... which gets rid of the ever-increasing indentation when traversing a tree structure. The syntax even allows additional statements in between for statements without requiring extra indentation, as in:
let yy =
seq {
for i in [ 1; 2 ] do
let i42 = i+42
for j in [ 3; 4 ] do
yield (i42,j)
}
Now, if only I could figure out why I thought these statements would require indentation...
Seq. groupBy takes a sequence and a function that generates a key from an element. The function is executed on each element of the sequence. Seq. groupBy returns a sequence of tuples, where the first element of each tuple is the key and the second is a sequence of elements that produce that key.
F# Sequence Workflows yield and yield! (pronounced yield bang) inserts all the items of another sequence into this sequence being built. Or, in other words, it appends a sequence. (In relation to monads, it is bind .)
Computation expressions in F# provide a convenient syntax for writing computations that can be sequenced and combined using control flow constructs and bindings. Depending on the kind of computation expression, they can be thought of as a way to express monads, monoids, monad transformers, and applicative functors.
The list is created on declaration, but elements in the sequence are created as they are needed. As a result, sequences are able to represent a data structure with an arbitrary number of elements: > seq { 1I ..
Just a few weeks ago, I wrote a paper with Don Syme that tries to explain some of the motivation behind the syntax choices in F# computation expressions (such as sequence expressions, asynchronous workflows and others). You can find it here. It does not give definite answer to your question, but it may help.
In general, when you have some type M<'T>
and you're defining computation builder, you can add methods For
and Bind
to enable for
and let!
syntax:
For : seq<'T> -> ('T -> M<'T>) -> M<'T>
Bind : M<'T> -> ('T -> M<'T>) -> M<'T>
The input for For
should always be some sequence seq<'T>
, while the input for Bind
should be the type M<'T>
for which you're defining it.
In sequence expressions, these two operations would have the same type and so they would have to do the same thing. Although sequence expressions could provide both, it is probably a good idea to allow just one, because using two different keywords for one thing would be confusing. A general principle is that code in comp { .. }
block should behave like normal code - and since for
exists in normal F# code, it makes sense to use the same syntax inside computation expression for seq<'T>
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With