Function templates in F#

后端 未结 4 748
我寻月下人不归
我寻月下人不归 2021-01-03 10:52

Let\'s say I am solving a particular problem and come up with a function

let function parameter1 ... = 
     a lot of adding, multiplying & so on with a         


        
相关标签:
4条回答
  • 2021-01-03 11:10

    One way to do it is combining inline keyword and generic bits from LanguagePrimitives module:

    let inline greatFunction param1 param2 = 
        let one = LanguagePrimitives.GenericOne
        let two = one + one
        (param1+one)/(param2*two)
    
    // Usage
    let f1 = greatFunction 4 2
    let f2 = greatFunction 4L 2L
    let f3 = greatFunction 4I 2I
    
    0 讨论(0)
  • 2021-01-03 11:12

    While not ideal, and kind of bypassing your main question, you can add type annotations to force the compiler's hand:

    let greatFunction (param1:int64) (param2:int64) : int64 = (param1+1)/(param2*2)
    

    Now of course there are no implicit conversions in F#, so you will need to add L to all numeric literals, but they will show up as compiler errors at least.

    0 讨论(0)
  • 2021-01-03 11:20

    Here's an article on generic, numeric calculations in F#. In general, you have two options:

    • Static member constraints
    • Global numeric associations (available in F# PowerPack)

    ...or you can combine these techniques.

    In your case, it sounds like static constraints will work.

    A simple example from that article:

    let inline halfSquare num =
       let res = LanguagePrimitives.DivideByInt num 2
       res * res
    
    0 讨论(0)
  • 2021-01-03 11:30

    I think Generic Arithmetic is common problem in .NET languages. There are many articles explaining different approaches and very soon I will post another one explaining mine which is similar to the solution you posted.

    Now, if you ask me if should you use it, I would say: as long as you understand what you are doing why not? I'm using it partially in production and have no issues at all, but because I care about run-time performance I use overloading to resolve everything at compile time. Then to speed up compile time I redefine basic math operators to operate in the same type, otherwise type signatures get really complicated and may take ages to compile.

    There are more things to consider but for your specific problem here is a sample code:

    open System.Numerics
    
    type FromInt = FromInt with
        static member ($) (FromInt, _:sbyte     ) = fun (x:int) -> sbyte      x
        static member ($) (FromInt, _:int16     ) = fun (x:int) -> int16      x
        static member ($) (FromInt, _:int32     ) = id
        static member ($) (FromInt, _:float     ) = fun (x:int) -> float      x
        static member ($) (FromInt, _:float32   ) = fun (x:int) -> float32    x
        static member ($) (FromInt, _:int64     ) = fun (x:int) -> int64      x
        static member ($) (FromInt, _:nativeint ) = fun (x:int) -> nativeint  x
        static member ($) (FromInt, _:byte      ) = fun (x:int) -> byte       x
        static member ($) (FromInt, _:uint16    ) = fun (x:int) -> uint16     x
        static member ($) (FromInt, _:char      ) = fun (x:int) -> char       x
        static member ($) (FromInt, _:uint32    ) = fun (x:int) -> uint32     x
        static member ($) (FromInt, _:uint64    ) = fun (x:int) -> uint64     x
        static member ($) (FromInt, _:unativeint) = fun (x:int) -> unativeint x
        static member ($) (FromInt, _:bigint    ) = fun (x:int) -> bigint     x
        static member ($) (FromInt, _:decimal   ) = fun (x:int) -> decimal    x
        static member ($) (FromInt, _:Complex   ) = fun (x:int) -> Complex(float x,0.0)  
    
    let inline fromInt (a:int) : ^t = (FromInt  $  Unchecked.defaultof< ^t>) a
    
    module NumericLiteralG =
        let inline FromZero() =LanguagePrimitives.GenericZero
        let inline FromOne() = LanguagePrimitives.GenericOne
        let inline FromInt32 (i:int)     = fromInt i
    
    
    // This will reduce the number of types inferred, will reduce compile time too.
    let inline (+) (a:^t) (b:^t) : ^t = a + b
    let inline (-) (a:^t) (b:^t) : ^t = a - b
    let inline (*) (a:^t) (b:^t) : ^t = a * b
    let inline (/) (a:^t) (b:^t) : ^t = a / b
    let inline (~-) (a:^t) : ^t = -a
    
    
    let inline halfSquare num =
       let res = num / 2G
       res * res
    
    let solve1 = halfSquare 5I 
    let solve2 = halfSquare 5.0
    let solve3 = halfSquare 5uy 
    
    // Define more generic math functions.
    
    0 讨论(0)
提交回复
热议问题