F# сравнивает размеченные объединения по идентификатору случая

Есть ли способ сравнить размеченные союзы по их идентификаторам регистра в F#?

type MyUnion =
| MyString of string
| MyInt of int

let x = MyString("hello")
let y = MyString("bye")
let z = MyInt(25)

let compareCases a b =
// compareCases x y = true
// compareCases x z = false
// compareCases y z = false

Как мне реализовать функцию compareCases общим способом?

т.е. что-то вроде следующего, но более общего (отражение в порядке):

let compareCases a b =
  match a with
  | MyString(_) -> match b with | MyString(_) -> true | _ -> false
  | MyInt(_) -> match b with | MyInt(_) -> true | _ -> false

person Grozz    schedule 22.05.2014    source источник
comment
Это очень похоже на stackoverflow.com/questions/12686911/   -  person John Palmer    schedule 22.05.2014
comment
Спасибо, это выглядит интересно, но у него есть все случаи, подкрепленные одним и тем же типом, и я не уверен, как это можно применить к моей ситуации с разными типами.   -  person Grozz    schedule 22.05.2014
comment
Если бы Tag было поддерживаемым открытым свойством, это был бы очень эффективный и простой способ сделать это.   -  person ildjarn    schedule 22.05.2014


Ответы (3)


Проблема с использованием GetType() заключается в том, что он терпит неудачу, если у вас есть 2 случая «без данных».

Вот один из способов сделать это: (отредактировано, потому что предыдущий UnionTagReader не кэшировался)

type MyDU =
    | Case1
    | Case2
    | Case3 of int
    | Case4 of int

type TagReader<'T>() =
    let tr = 
        assert FSharpType.IsUnion(typeof<'T>)
        FSharpValue.PreComputeUnionTagReader(typeof<'T>, System.Reflection.BindingFlags.Public)

    member this.compareCase (x:'T) (y:'T) =
        (tr x) = (tr y)

let tr = TagReader<MyDU>()

let c1 = Case1
let c2 = Case2
let c3 = Case3(0)
let c3' = Case3(1)
let c4 = Case4(0)

assert (c1.GetType() = c2.GetType() )  //this is why you can not use GetType()

assert tr.compareCase c1 c1
assert not (tr.compareCase c1 c2)
assert tr.compareCase c3 c3'
assert not (tr.compareCase c3 c4)
person jyoung    schedule 22.05.2014
comment
+1, хотя, поскольку ваш ответ - единственный правильный ответ, я хотел бы проголосовать более одного раза. Но что-то здесь кажется странным с выводом типов — я бы не подумал, что вам понадобятся два общих объявления, но они вам нужны. И каким-то образом мы получаем tagReader как 'UnionType -> int, хотя PreComputeUnionTagReader не является общим obj -> int. Можете ли вы объяснить что-либо из этого или указать на какую-либо документацию, которая могла бы это сделать? - person phoog; 31.05.2014
comment
phoog: У меня нет документации. Я также обнаружил, что статический вывод приводил к сбою приведения из obj -> int к 'UnionType -> int. Поэтому я перемещаю код туда, где он не подвергался статическому выводу. Мое смутное предположение состоит в том, что когда я переместил его, он перешел от правил функционального статического вывода к правилам дисперсии объектов .net. - person jyoung; 01.06.2014

Прежде всего, вы можете улучшить свой пример следующим образом:

let compare = function
| MyString _, MyString _, | MyInt _, MyInt _ -> true
| _ -> false

Но вот лучший способ (с минимальным размышлением!):

let compare a b = a.GetType () = b.GetType ()
person Jwosty    schedule 22.05.2014

Это должно помочь

open Microsoft.FSharp.Reflection

type MyUnion =
    | MyString of string
    | MyInt of int

let x = MyString("hello")
let y = MyString("bye")
let z = MyInt(25)

let compareCases a b =
    FSharpValue.GetUnionFields (a, a.GetType()) |> fst
        = (FSharpValue.GetUnionFields (b, b.GetType()) |> fst)

хотя, чтобы что-то делать со значениями, вам все равно нужно будет сопоставлять шаблоны, так что я не совсем вижу в этом смысл, если честно.

person Daniel Fabian    schedule 22.05.2014