Как я могу вернуть обратно тип, который был ранее?

Очень часто при написании родового кода в F # я сталкиваюсь с ситуацией, подобной этой (я знаю, что это довольно неэффективно, просто для демонстрационных целей):

let isPrime n =
    let sq = n |> float |> sqrt |> int
    {2..sq} |> Seq.forall (fun d -> n % d <> 0)

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

let inline isPrime (n:^a) =
    let two = LanguagePrimitives.GenericOne + LanguagePrimitives.GenericOne
    let sq = n |> float |> sqrt |> int
    {two..sq} |> Seq.forall (fun d -> n % d <> LanguagePrimitives.GenericZero)

Приведенный выше код не будет компилироваться из-за того, что верхний предел последовательности является поплавком. В некотором роде, я мог бы просто вернуться к int, например.

Но компилятор не позволит мне использовать какие-либо из них:

  • let sq = n |> float |> sqrt :> ^a
  • let sq = n |> float |> sqrt :?> ^a

и эти два ведут к InvalidCastException:

  • let sq = n |> float |> sqrt |> box |> :?> ^a
  • let sq = n |> float |> sqrt |> box |> unbox

Кроме того, upcast и downcast запрещены.

let sq = System.Convert.ChangeType(n |> float |> sqrt, n.GetType()) :?> ^a работает, но мне кажется очень громоздким.

Есть ли способ, который я забыл или мне действительно нужно использовать последнюю версию? Потому что последний будет разбиваться на bigint, что мне нужно довольно часто.

Ответ 1

С трюком FsControl мы можем определить общую функцию fromFloat:

open FsControl.Core

type FromFloat = FromFloat with
    static member instance (FromFloat, _:int32 ) = fun (x:float) -> int x
    static member instance (FromFloat, _:int64 ) = fun (x:float) -> int64 x
    static member instance (FromFloat, _:bigint ) = fun (x:float) -> bigint x
let inline fromFloat (x:float):^a = Inline.instance FromFloat x

let inline isPrime (n:^a) =
    let two = LanguagePrimitives.GenericOne + LanguagePrimitives.GenericOne
    let sq = n |> float |> sqrt |> fromFloat
    {two..sq} |> Seq.forall (fun d -> n % d <> LanguagePrimitives.GenericZero)

printfn "%A" <| isPrime 71
printfn "%A" <| isPrime 6L
printfn "%A" <| isPrime 23I

Inline.instance был определен здесь.