5

Is it possible to use active patterns within discrimated union type declarations?

To be more precise, consider the following toy example:

type T = 
    | A of int
    | B

let (|Negative|_|) t = 
    match t with
    | A n when n < 0 -> Some ()
    | _ -> None

let T_ToString = function
    | Negative () -> "negative!"
    | _ -> "foo!"

Now suppose I want to override ToString() in T. Inside T's type declaration I can't refer to T_ToString since T_ToString is not yet declared at that point. I can't move the active pattern and T_ToString before ToString() because T is not yet declared at that point. But this doesn't work either:

type T = 
    | A of int
    | B

    static member (|Negative|_|) t = 
        match t with
        | A n when n < 0 -> Some ()
        | _ -> None

    override this.ToString () = 
        match this with
        | Negative () -> "negative!"
        | _ -> "foo!"
John Saunders
  • 160,644
  • 26
  • 247
  • 397
Mo B.
  • 5,307
  • 3
  • 25
  • 42
  • I have edited your title. Please see, "[Should questions include “tags” in their titles?](http://meta.stackexchange.com/questions/19190/)", where the consensus is "no, they should not". – John Saunders Dec 12 '12 at 15:25
  • [This answer](http://stackoverflow.com/a/1834344/974789) may be relevant: *"Active patterns should not be used as members. The fact that these compile at all is a compiler bug"* – Be Brave Be Like Ukraine Dec 12 '12 at 16:32

3 Answers3

4

Your question suggests that testing for negative is an intrinsic operation of T, so it should be part of its definition. Defining a property is one way to do that:

type T = 
  | A of int
  | B

  member this.IsNegative = 
    match this with
    | A n -> n < 0
    | _ -> false

  override this.ToString() = 
    if this.IsNegative then "negative!"
    else "foo!"

I'm not sure the active pattern is still needed, but if it is it's trivial:

let (|Negative|_|) (x: T) = if x.IsNegative then Some() else None
Daniel
  • 47,404
  • 11
  • 101
  • 179
3

It's not the nicest solution, but you can do this:

type T = 
    | A of int
    | B

    static member internal ActivePattern t =
        match t with
        | A n when n < 0 -> Some ()
        | _ -> None

    override this.ToString () = 

        let (|Negative|_|) = T.ActivePattern

        match this with
        | Negative () -> "negative!"
        | _ -> "foo!"

let (|Negative|_|) = T.ActivePattern
Gustavo Guerra
  • 5,319
  • 24
  • 42
2

OK, I think I found a solution: first declare the type, then declare the active pattern outside of it, and finally augment the type with an override implementation of ToString().

type T = 
    | A of int
    | B

let (|Negative|_|) t = 
    match t with
    | A n when n < 0 -> Some ()
    | _ -> None

type T with
    override this.ToString() = 
        match this with
        | Negative () -> "negative!"
        | _ -> "foo!"

However, this is not very nice as I get the warning

warning FS0060: Override implementations in augmentations are now deprecated. Override implementations should be given as part of the initial declaration of a type.
Mo B.
  • 5,307
  • 3
  • 25
  • 42
  • I think this suggests a design problem: an intrinsic operation, `ToString`, depends on an auxiliary function, `(|Negative|_|)`. There's good reason this is awkward to do. – Daniel Dec 12 '12 at 17:09