2017-05-04 9 views
2

"準汎用"関数を定義しようとしています。それを完全に汎用的にするのではなく、 "整数"型(byte、sbyte、int16、uint16、int、uint32、int64、uint64、bigint)に対してのみ動作させたいと思います。F#複数の型の関数型注釈

これを関数定義の型アノテーションに挿入するにはどうすればよいですか?私は実際に(おそらく一般化の損失なしにのみ3種類を使用して)動作するように、次のコードを書き換えるだろうか、明確にする:すべての

let square (x: int|int64|bigint) = 
    x * x 
+3

これは、[制約](https://docs.microsoft.com/en-us/dotnet/articles/fsharp/language-reference/generics/constraints)で達成できるかもしれない - それがしたいかどうかしかし、私はわかりません。 –

答えて

6

まず、標準の.NETを使用して、このようなタイプの制約を解決する方法はありません実行時のジェネリックス。

F#では、コンパイル時に解決し、適切な関数呼び出しをインラインで挿入することで、制限された形式の制約を表現することができます。これはstatically resolved type parametersを使用します。

それはあなたが説明した場合のために非常に簡単です、あなただけ書くことができます。

let inline square x = x * x 

これは、定義された*演算子を持つ任意のタイプ'Tのために動作します。

特定の静的/メンバー制約を明示的に適用することもできますが、これにはより醜い構文が必要です。

let inline id item = 
    (^T : (member Id : int) (item)) 

この例関数は、タイプintIdプロパティを公開任意のタイプで動作します。


更新:あなたが記述している特定のユースケースに基づいて、あなたは本当に、クラスを入力します。これはあなたが正確に指定することができます

type Marker = 
    |Marker 

    static member Multiply (marker : Marker, numX : int, numY : int) = 
     numX * numY 
    static member Multiply (marker : Marker, numX : int64, numY : int64) = 
     numX * numY 

let inline multiply x y = 
    ((^T or ^U) : (static member Multiply : ^T * ^U * ^U -> ^S) (Marker, x, y)) 

multiply 5 7 
multiply 5L 7L 

注意:これらは本当に(いくつかのハードコードされた例は別に)のF#には存在しませんが、あなたはマーカータイプとメンバー制約を使用してそれらをシミュレートすることができ、ここでは一例であり、機能を許可するタイプを選択します。

+0

私は実際に数字を二乗しようとしていません、私はちょうどそれが簡単な例と思った。素数性テストや特定の因子分解技術を実行するいくつかの関数を作成しようとしています。因数分解が意味をなさないため、アルゴリズムのいくつかがうんざりになるため、これらの関数が浮動小数点数で動作することを許可したくありません。 – Talmage

+0

@Talmageあなたが探しているものは更新された回答ですか? – TheInnerLight

+0

はい、これは実際に私が昼食を食べていたときに考えていたアプローチでした。私は今夜​​の反応を完全に消化して、質問に答えさせるようにします。 – Talmage

2

a)はあなたがそれらに適用する演算子/メソッドをサポートし、既に使用しているタイプは、この場合、
は、単にあなたの関数の前にinlineを追加するには、問題の3つのアプローチは基本的にあります。そして、)

B幸せであるあなたは、あなたが拡張メソッドを使用せずに関数定義で新しいメンバーを定義することができますです
を使用しているタイプを完全に制御できます。この場合、あなたはこのかなり奇妙探し構文

let inline mult (x:^T) (y:^T) = (^T : (static member Mult: ^T -> ^T -> ^T) (x, y)) 

でジェネリック型の制約を使用して

type MyInt16 = MyInt16 of int 
    with 
    static member Mult(x, y) = 
     match x,y with 
     | MyInt16 x', MyInt16 y' -> MyInt16 (x' * y') 

type MyInt32 = MyInt32 of int 
    with 
    static member Mult(x, y) = 
     match x,y with 
     | MyInt32 x', MyInt32 y' -> MyInt32 (x' * y') 

とインライン関数必要なものを実装する各クラスのメソッドを定義して、あなたは

let a = MyInt16 2 
let b = MyInt16 3 

let c = mult a b 
をテスト

これは機能します。異なるタイプを使用すると何が起こるかを確認してください。

let d = mult a (MyInt32 3) 

上記のエラーはあなたに表示されます。

C)あなたがタイプ内のメソッドを定義することはできませんが、あなたが拡張メソッドを使用する必要がありますです、あなたのタイプ
を完全に制御を持っていません。拡張メソッドの問題点は、汎用タイプの制約を使用するインライン関数を持つ を使用できないことです。あなたがより良いparameter approach I described here

type MultParam = 
    | MyInt16Param of System.Int16 
    | MyInt32Param of System.Int32 
with 
    static member op_Implicit(x: System.Int16) = MyInt16Param x 
    static member op_Implicit(x: System.Int32) = MyInt32Param x 

にフォールバックこの場合、その後、あなたのラッパー型

let inline (!>) (x:^a) : ^b = ((^a or ^b) : (static member op_Implicit : ^a -> ^b) x) 

と実装を追加するにあなたの着信の種類を変換し、一般的な制約に再びインライン関数を定義します。我々は

let inline mult' (x: ^T) (y: ^T) : ^T = 
    let x' = !> x 
    let y' = !> y 
    let r = 
     match x', y' with 
     | MyInt16Param x'', MyInt16Param y'' -> x'' * y'' |> box 
     | MyInt32Param x'', MyInt32Param y'' -> x'' * y'' |> box 
     | _ -> failwith "Not possible" 
    r :?> _ 

パターンマッチングを使用する必要があるとして、この時間ビットwordierは、今度は、この作品再び

let e = mult' (int16(2)) (int16(3)) 

をテストしてみましょう。異なるタイプを使用すると何が起こるかを見てみましょう。

let f = mult' (int16(2)) (int32(3)) 

もう一度エラーがあります。

オプションB)は、基本的に
オプションc)のように多型変異体

1

制約OCamlsに近いHaskells型クラスの機能のエミュレーションであり、F#型システムの2つの機能を組み合わせた効果のために速記(Statically Resolved Type ParametersメンバーConstraints)が役に立ちます。彼らの雇用は、コンパイル時に複数の冗長なエラーメッセージではあるが、互換性のない型の除外を許可します。

module MyInt = 
    type MyInt<'T> = private MyInt of 'T 
    type Wrap = Wrap with 
     static member ($) (Wrap, value : int ) = MyInt value 
     static member ($) (Wrap, value : int64) = MyInt value 
     static member ($) (Wrap, value : bigint) = MyInt value 
    let inline create value : MyInt<_> = Wrap $ value 

let x = MyInt.create 1 // MyInt.MyInt<int> 
let y = MyInt.create 1I // MyInt.MyInt<bigint> 
let z = MyInt.create 1.0 // Error No overloads match for method 'op_Dollar'. ... 

特殊なドメインへのエントリに制約を付けるのが不便な場合は、これをやめることもできます。

module MyInt' = 
    type MyInt<'T> = private MyInt of 'T 
    type Unwrap = Unwrap with 
     static member ($) (Unwrap, MyInt(value : int )) = value 
     static member ($) (Unwrap, MyInt(value : int64)) = value 
     static member ($) (Unwrap, MyInt(value : bigint)) = value 
    let inline myInt value = Unwrap $ value 
    let x, y, z = MyInt 1, MyInt 1I, MyInt 1.0 

let a = MyInt'.myInt MyInt'.x // int 
let b = MyInt'.myInt MyInt'.y // bigint 
let c = MyInt'.myInt MyInt'.z // Error No overloads match for method 'op_Dollar'. ... 
関連する問題