web-dev-qa-db-ja.com

ジェネリックパラメータータイプの関数

複数のタイプのパラメーター(intやint64など)で機能する関数を定義する方法を理解しようとしています。私が理解しているように、F#では関数のオーバーロードは不可能です(確かにコンパイラは文句を言います)。たとえば、次の関数を考えてみましょう。

let sqrt_int = function
    | n:int   -> int (sqrt (float n))
    | n:int64 -> int64 (sqrt (float n))

コンパイラはもちろん構文が無効であると文句を言います(パターンマッチングの型制約はサポートされていないようです)が、これは私が達成したいことを示していると思います:いくつかのパラメータ型で動作し、それに応じた値を返す関数タイプ。これは、ジェネリック型/型推論/パターンマッチングのいくつかの組み合わせを使用するF#で可能であると感じていますが、構文がわかりませんでした。私も:?を使ってみましたパターンマッチングブロックの演算子(動的型テスト)およびwhen句ですが、それでもすべての種類のエラーが発生します。

私はこの言語にかなり慣れていないので、ここでは不可能なことをしようとしている可能性があります。別の解決策があるかどうか教えてください。

46
Noldorin

オーバーロードは通常、型推論言語のバガブーです(少なくとも、F#のように、型システムが型クラスを含むほど強力ではない場合)。 F#にはいくつかの選択肢があります。

  • メソッド(型のメンバー)にオーバーロードを使用します。この場合、オーバーロードは他の.Net言語と同じように機能します(呼び出しがパラメーターの数/タイプによって区別できる場合は、アドホックなオーバーロードメンバーを使用できます)
  • 関数のアドホックオーバーロードには、「インライン」、「^」、および静的メンバー制約を使用します(これは、int/floatなどで動作する必要があるさまざまな数学演算子のほとんどです。ここでの構文は奇妙です。これはF#ライブラリ以外はほとんど使用されていません)
  • 追加のDictionary-of-Operationsパラメーターを渡すことにより、型クラスをシミュレートします(これは、任意のユーザー定義型のさまざまな数学アルゴリズムを一般化するために、F#PowerPackライブラリの1つでINumericが行うことです)
  • 動的型付けにフォールバックします(「obj」パラメーターを渡し、動的型テストを実行し、不正な型のランタイム例外をスローします)

あなたの特定の例では、私はおそらくメソッドのオーバーロードを使用するでしょう:

type MathOps =
    static member sqrt_int(x:int) = x |> float |> sqrt |> int
    static member sqrt_int(x:int64) = x |> float |> sqrt |> int64

let x = MathOps.sqrt_int 9
let y = MathOps.sqrt_int 100L
61
Brian

これは機能します:

type T = T with
    static member ($) (T, n:int  ) = int   (sqrt (float n))
    static member ($) (T, n:int64) = int64 (sqrt (float n))

let inline sqrt_int (x:'t) :'t = T $ x

静的制約とオーバーロードを使用して、引数のタイプをコンパイル時にルックアップします。

静的制約は、演算子(operator $この場合)しかし、それは常に手で書くことができます:

type T = T with
    static member Sqr (T, n:int  ) = int   (sqrt (float n))
    static member Sqr (T, n:int64) = int64 (sqrt (float n))

let inline sqrt_int (x:'N) :'N = ((^T or ^N) : (static member Sqr: ^T * ^N -> _) T, x)

これについての詳細 ここ

18
Gustavo

はい、これは可能です。 このhubFSスレッド を見てください。

この場合、解決策は次のようになります。

let inline retype (x:'a) : 'b = (# "" x : 'b #)
let inline sqrt_int (n:'a) = retype (sqrt (float n)) : 'a

警告:コンパイル時の型チェックはありません。つまりsqrt_int "blabla"は正常にコンパイルされますが、実行時にFormatExceptionが発生します。

14

実行時型チェックを使用する別の方法は次のとおりです...

let sqrt_int<'a> (x:'a) : 'a = // '
    match box x with
    | :? int as i -> downcast (i |> float |> sqrt |> int |> box)
    | :? int64 as i -> downcast (i |> float |> sqrt |> int64 |> box)
    | _ -> failwith "boo"

let a = sqrt_int 9
let b = sqrt_int 100L
let c = sqrt_int "foo" // boom
9
Brian

すでに提供されている正解を損なうことはありませんが、実際にはパターンマッチングで型制約を使用できます。構文は次のとおりです。

| :? type ->

または、型チェックとキャストを組み合わせたい場合:

| :? type as foo ->
2
Joel Mueller