Open smoothdeveloper opened 1 year ago
An idea how this could be implemented in a library if we had a SeqBuilder:
// Hint on yield!
// ---------------
// The usage of yield! looks non-idiomatic,
// since it usually is used to yield the computation's base type
// (e.g. seq<'a>), and now it's intention is extended to
// yield a "seq-like" value option<'a>.
// Why yield! has to be used in any case:
// ------------------------------------------
// yield can't be used here (so I used yield!, even though
// it might seem unfamiliar in the first place - see comment above)
// because of ambiguities in overloads.
// In other words: All yielded values must match exactly
// one overload, which would not be the case having 2 yield-overloads
// with 'a and 'a option (both would match yielding an 'a option,
// even though one is more specific that the other).
// Why this sample can't work with the `seq` implementation:
// -----------------------------------------------------------------
// As far as I know, there's no SeqBuilder in F# core that could
// be used to augment (it's baked into the compiler),
// so this is a non-optimized custom SeqBuilder for
// demo purposes only.
type SeqBuilder() =
member _.For(m: seq<_>, f) = Seq.collect f m
member _.Yield(v) = Seq.singleton v
member _.Zero<'x>() = Seq.empty<'x>
member _.Combine(a, delayedB) = Seq.append a (delayedB ())
member _.Delay(f) = f
member _.Run(f) = f()
// here we go...
member _.YieldFrom(m: option<_>) =
match m with
| Some v -> Seq.singleton v
| None -> Seq.empty
let seq = SeqBuilder()
// -------------------------------------------
type Data = {
a: string
b: string option
c: string option
}
let getValues data =
seq {
data.a
yield! data.b
yield! data.c
}
getValues { a = "a"; b = Some "b"; c = None }
// val it: seq ["a"; "b"]
A way of approaching a production ready solution could be to implement a performant seq builder utilizing "resumable code", or by perf-tuning a "normal" builder with InlineIfLambda and other techniques, then making that builder available in a library and shadow the seq
keyword.
Is there a reason to have yield!
but not for ... in
?
I was thinking of creating an issue for ValueOption<'t>
to support IEnumerable<'t>
(which doesn't have the null
problem supporting it that Option<'t>
has). But the main use cases would be for ... in
and yield!
so if those are implemented then it wouldn't be needed.
Isn't
yield! Option.toList data.b
already a way of doing this?
I'd like usage of
yield!
invalues
to be syntax sugar (0 cost abstraction) forI'd also like to figure if this can be made possible on types without them going to extent of implementing iterator patterns, applying to
System.Nullable
,ValueOption
,Result
would make sense in terms of FSharp.Core / compiler implementation if the suggestion is appealing.It could be something that is in coding guidelines for types that implement
map
, where the semantic makes sense, if the suggestion can be taken from standpoint of enabling this on adhoc type (which would be great).The existing way of approaching this problem in F# is ...
optionSeq
(and others for other data structures) with overloadsOption
(and other relevant value containers) type to implementIEnumerable
I'm not sure the alternatives can bring it to a 0 cost abstraction.
Pros and Cons
The advantages of making this adjustment to F# are:
The disadvantages of making this adjustment to F# are:
yield!
currently works (only withIEnumerable
AFAIU)yield!
for
construct (which shouldn't be the case in context of this sole feature and #716)Extra information
Estimated cost (XS, S, M, L, XL, XXL): between S and L depending on wether we just want this for option/voption/nullable and not supporting the same on adhoc types.
Related suggestions: (#716)
Affidavit (please submit!)
Please tick this by placing a cross in the box:
Please tick all that apply: