Как мне сделать функцию с параметром в зависимости от типа от поставщика типов? - PullRequest
0 голосов
/ 18 июня 2020

Я новичок в F #.

Я пытаюсь создать базовую служебную функцию, которая зависит от типа, который использует поставщик типа:

open FSharp.Text.RegexProvider

type DotnetVersionRegex = Regex< @"dotnet (?<Version>.*)" >
type OutlookVersionRegex = Regex< @"Outlook (?<Version>.*)" >

let parseOptionalValue matcher values =
    values
    |> Seq.map (fun x -> matcher x)
    |> Seq.tryFind (fun x ->
        (^T : (member Success: bool) x) && (^T : (member Version: System.Text.RegularExpressions.Group) x).Success) // x.Success && x.Version.Success
    |> function
        | Some m -> Some ((^T : (member Version: System.Text.RegularExpressions.Group) m).Value) // m.Version.Value
        | None -> None

// The first time I use "parseOptionalValue" it produces a warning "This construct causes code to be less generic...
// […] the type variable 'T has been constrained to be type 'Regex<...>.MatchType'.
let dotnetVersion = parseOptionalValue (DotnetVersionRegex().TypedMatch)

// The second time I use "parseOptionalValue",
// it produces an error "This expression was expected to have type
// <the type of the parameter passed in the first use just above (DotnetVersionRegex().TypedMatch),
//     that is, the Regex type provider with the pattern of my first regex>"
let outlookVersion = parseOptionalValue (OutlookVersionRegex().TypedMatch)

Для справки: поскольку он может быть более разборчивым, вот чего я пытаюсь избежать:

let dotnetVersion values =
    values
    |> Seq.map (fun x -> DotnetVersionRegex().TypedMatch x)
    |> Seq.tryFind (fun x -> x.Success && x.Version.Success)
    |> function
        | Some m -> Some m.Version.Value
        | None -> None
let outlookVersion values =
    values
    |> Seq.map (fun x -> OutlookVersionRegex().TypedMatch x)
    |> Seq.tryFind (fun x -> x.Success && x.Version.Success)
    |> function
        | Some m -> Some m.Version.Value
        | None -> None

Вопросы:

  • Кажется ли мой код чем-то правильным и чего-то не хватает (что? ), или я иду в неправильном направлении?
  • В более общем плане, как я могу создать функцию с параметром в зависимости от типа от поставщика типов? Возможно ли без потери безопасности типа?

Ответы [ 2 ]

0 голосов
/ 19 июня 2020

Думаю, я нашел способ заставить его работать: он компилируется, когда я добавляю ключевое слово inline в свою parseOptionalValue функцию!

Я, честно говоря, не очень хорошо понимаю причину, по которой это не было ' t, в первую очередь, и точная роль ключевого слова inline ...

У меня все еще есть слабая надежда, что есть лучший способ написать строго типизированную версию моего первоначального примера, так что почувствуйте бесплатно предлагать другие ответы ...

Вот очищенная и рабочая версия:

open FSharp.Text.RegexProvider

type DotnetVersionRegex = Regex< @"dotnet (?<Version>.*)" >
type OutlookVersionRegex = Regex< @"Outlook (?<Version>.*)" >

let inline parseOptionalValue matcher values =
    values
    |> Seq.map (fun x -> matcher x)
    |> Seq.tryFind (fun x ->
        (^T : (member Success: bool) x) && (^T : (member Version: System.Text.RegularExpressions.Group) x).Success) // x.Success && x.Version.Success
    |> function
        | Some m -> Some ((^T : (member Version: System.Text.RegularExpressions.Group) m).Value) // m.Version.Value
        | None -> None

let dotnetVersion (values: string array) = parseOptionalValue (DotnetVersionRegex().TypedMatch) values
let outlookVersion (values: string array) = parseOptionalValue (OutlookVersionRegex().TypedMatch) values

let printSome = function
    | Some x -> printfn "%O" x
    | None -> printfn "None"

let values = "dotnet 4.5;Outlook 16".Split(';')
dotnetVersion values |> printSome
outlookVersion values |> printSome
0 голосов
/ 18 июня 2020

Вы можете сделать что-то вроде этого:

let parseOptionalValue<'T when 'T :> System.Text.RegularExpressions.Match> (matcher:string->'T) (values:string seq) : 'T option =
    values
    |> Seq.map (fun x -> (matcher x))
    |> Seq.tryFind (fun x -> x.Success)

let inline v (x:^T) = (^T : (member Version : System.Text.RegularExpressions.Group) x)

let inline parseVersion matcher values =  parseOptionalValue matcher values
                                          |>Option.bind (fun x->let group = v x  
                                                                if group.Success then Some group.Value
                                                                else
                                                                None)

let v1 = parseVersion (DotnetVersionRegex().TypedMatch) [ "dotnet 3.5" ]

let v2 = parseVersion (OutlookVersionRegex().TypedMatch) [ "Outlook 3.5" ]


match v1 with
| Some(v) -> printfn "version %s" v
| None -> printfn "none"

Выводы:

version 3.5

С другой стороны, это перебор, если это ваш реальный сценарий, так как вы создадите сингл Версия регулярного выражения

type VersionRegex = Regex< @"(?<Product>\\w+)(?<Version>.*)" >
...