-
Notifications
You must be signed in to change notification settings - Fork 847
Description
I was playing a bit with the new feature that allows implicit yields and realized that it's a good thing to write custom collections, like semigroups as the CE can't be empty.
But soon realized that it requires a Zero method, even when it is not used at all.
Repro steps
type NonEmpty<'t> = list<'t> // for simplicity
type NelBuilder () =
member __.Zero () = invalidOp "A NonEmptyList doesn't support the Zero operation."
member __.Combine (a: NonEmpty<'T>, b) = a @ b
member __.Yield x = List.singleton x
member __.Delay expr = expr () : NonEmpty<'T>
let nel = NelBuilder ()
let x = nel {1; 2; 3}
// val x : NonEmpty<int> = [1; 2; 3]
type NelBuilder2 () =
// member __.Zero () = invalidOp "A NonEmptyList doesn't support the Zero operation."
member __.Combine (a: NonEmpty<'T>, b) = a @ b
member __.Yield x = List.singleton x
member __.Delay expr = expr () : NonEmpty<'T>
let nel2 = NelBuilder2 ()
let y = nel2 {1; 2; 3}
// ~vsA01F.fsx(22,21): error FS0708: This control construct may only be used if the computation expression builder defines a 'Zero' method
type NelBuilder3 () =
// member __.Zero () = invalidOp "A NonEmptyList doesn't support the Zero operation."
member __.Combine (a: NonEmpty<'T>, b) = a @ b
member __.Yield x = List.singleton x
member __.Delay expr = expr () : NonEmpty<'T>
let nel3 = NelBuilder3 ()
let y = nel3 {1; 2; 3; ()}
// ~vsA01F.fsx(33,24): error FS0708: This control construct may only be used if the computation expression builder defines a 'Zero' method
Expected behavior
Sample with nel compiles although Zero is not called.
Sample with nel2 compiles as Zero is not called.
Sample with nel3 doesn't compile as Zero is called.
Actual behavior
Sample with nel compiles although Zero is not called.
Sample with nel2 doesn't compile even though Zero is never called.
Sample with nel3 doesn't compile as Zero is called.
Known workarounds
Define a Zero method that throws a runtime exception but it defeats a bit the purpose of a compile-time non-empty-collection, as sample 1 would start compiling but failing at runtime.
Related information
Original comment: fsharp/fslang-suggestions#643 (comment)