Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I cast a back to a type a value was before?

Very often when writing generic code in F# I come by a situation similar to this (I know this is quite inefficient, just for demonstration purposes):

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

For many problems I can use statically resolved types and get even a performance boost due to 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)

The code above won't compile because of the upper sequence limit being a float. Nongenerically, I could just cast back to int for example.

But the compiler won't let me use any of these:

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

and these two lead to a InvalidCastException:

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

Also, upcast and downcast are forbidden.

let sq = System.Convert.ChangeType(n |> float |> sqrt, n.GetType()) :?> ^a works, but seems very cumbersome to me.

Is there a way that I overlooked or do I really have to use the last version? Because the last one will also break for bigint, which I need quite often.

like image 747
primfaktor Avatar asked Aug 28 '14 12:08

primfaktor


1 Answers

With the trick from FsControl, we can define generic function 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 was defined here.

like image 90
jindraivanek Avatar answered Oct 23 '22 15:10

jindraivanek