Сопоставление с шаблоном F # на карте универсального типа - PullRequest
0 голосов
/ 27 апреля 2019

Это работает:

// sample objects    
let dctStrDbl = [("k1",1.0);  ("k2",2.0)]  |> Map.ofList
let dctStrStr = [("k1","v1"); ("k2","v2")] |> Map.ofList
let lstMisc   = [1; 2; 3]

let testStrDbl (odico : obj) : bool =
   match odico with
   | :? Map<string,double> as d -> true
   | _                          -> false

let testTrue  = testStrDbl (box dctStrDbl)    // this evaluates to true
let testFalse = testStrStr (box dctStrStr)    // this evaluates to false
let testMiscFalse = testStrDbl (box lstMisc)  // evaluates to false

Однако я бы хотел сопоставить шаблон с универсальной картой типа Map<'k,'v> (а не с картой конкретного типа, такой как Map<string,double>).В псевдокоде:

let testGenMap (odico : obj) : bool =
    match odico  with
    | :? Map<'k,'v> as d -> true
    | _                  -> false

, но это не работает, так как они оба оценили бы как ложное

let testStrDblGen = testGenMap (box dctStrDbl)
let testStrDblGen = testGenMap (box dctStrStr)

Мой вопрос: есть ли способ сопоставления с универсальным Map<'k,'v>?

= РЕДАКТИРОВАТЬ =======

Возможно, мне следовало бы дать какой-то дополнительный контекст.Что мне действительно нужно, так это

let findGen (odico : obj) (defVal : 'a) (apply : (Map<'k,'v> -> 'a)) : 'a = 
    match odico with
    | :? Map<'k,'v> as d -> apply d
    | _                  -> defVal // the object is not of the expected type

... где я могу восстановить универсальные типы 'k и 'v.В этом смысле предлагаемое решение nilekirk не будет работать как есть.

1 Ответ

2 голосов
/ 27 апреля 2019

На универсальной карте нет встроенного способа сопоставления с образцом.

Что вы можете сделать, это использовать отражение и активный паттерн:

let (|IsMap|_|) (x: obj) =
    if x.GetType().Name.StartsWith("FSharpMap") then Some () else None

let test = function
    | IsMap -> true
    | _ -> false

Map.empty<int,string> |> test // true
[1] |> test // false

= РЕДАКТИРОВАТЬ =======

Видя ваши изменения выше, возможно, будет работать следующее:

let isMap<'k,'v when 'k : comparison> (m: obj) =
    typeof<Map<'k,'v>> = m.GetType()

let findGen odico defVal (apply : Map<'k,'v> -> 'a) =
    if odico |> isMap<'k,'v> then
        odico |> unbox<Map<'k,'v>> |> apply
    else
        defVal

let apply (x: Map<int,string>) = "the apply result"

findGen ([1,"one"] |> Map.ofList) "defVal" apply // "the apply result"
findGen (["one",1] |> Map.ofList) "defVal" apply // "defval"
findGen [1] "defVal" apply // "defval"
...