Skip to content

Zero method required in CEs, even when not used #8587

@gusty

Description

@gusty

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)

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugImpact-Low(Internal MS Team use only) Describes an issue with limited impact on existing code.

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions