web-dev-qa-db-ja.com

関数シグネチャhaskellのオーバーロード

コンパイルすると、次のエラーメッセージが表示されます。

重複する型の署名:
weightedMedian.hs:71:0-39:findVal :: [ValPair]->ダブル->ダブル
weightedMedian.hs:68:0-36:findVal :: [ValPair]-> Int-> Double

私の解決策は、findValIとfindValDを用意することです。ただし、findValIはInt型をDoubleに変換し、findValDを呼び出すだけです。

また、Num(Int、Double)の型でパターンマッチングを行うことができないため、型のシグネチャを

findVal :: [ValPair] -> Num -> Double   

多くの言語では、別の名前は必要ありません。 Haskellで異なる名前が必要なのはなぜですか?これを言語に追加するのは難しいでしょうか?それともドラゴンはいますか?

23
Tim Perry

アドホック多相性(および名前のオーバーロード)は、タイプクラスによってHaskellで提供されます。

class CanFindVal a where
          findVal :: [ValPair] -> a -> Double

instance CanFindVal Double where
     findVal xs d = ...

instance CanFindVal Int where
     findVal xs d = findVal xs (fromIntegral d :: Double)

この場合、findVal "really"にはDoubleが必要なので、常にdoubleをとる必要があり、intを渡す必要がある場合は、呼び出しサイトでfromIntegralを使用するだけです。一般に、無差別ではなく、実際に異なる動作やロジックが関係している場合に型クラスが必要になります。

33
sclv

両方をサポートするfindVal :: [ValPair] -> Double -> DoubleおよびfindVal :: [ValPair] -> Int -> Doubleアドホック多相性( http://www.haskell.org/haskellwiki/Ad-hoc_polymorphism を参照)が必要ですが、これは一般的に危険です。その理由は、アドホック多相性により、同じ構文でセマンティクスを変更できるためです。

Haskellはいわゆるパラメトリック多型を好みます。これは、型変数がある型シグネチャで常に見られます。

Haskellは、型クラスを介してアドホック多相性のより安全なバージョンをサポートしています。

3つのオプションがあります。

  1. 明示的な関数名を使用して、実行していることを続行します。これは合理的であり、openglなどの一部のcライブラリでも使用されています。
  2. カスタム型クラスを使用します。これはおそらく最良の方法ですが、重く、かなりの量のコードが必要です(haskellsによる非常にコンパクトな標準による)。コードについてはsclvの答えを見てください。
  3. 既存の型クラスを使用してみて、(GHCを使用している場合)特殊化されたパフォーマンスを取得してください。

このような:

findVal :: Num a => [ValPair] -> a -> Double
{-# SPECIALISE findVal :: [ValPair] -> Int -> Double #-}
{-# SPECIALISE findVal :: [ValPair] -> Double -> Double #-}
findVal = ...
16
Philip JF

HaskellはC++スタイルのオーバーロードをサポートしていません(型クラスでもある程度サポートしていますが、同じようには使用していません)。そして、ええ、それを追加することに関連するいくつかのドラゴンがあり、主に型推論に関係しています(指数関数的な時間になるか、決定不能になるか、そのようなものになります)。ただし、このような「便利な」コードがHaskellで表示されることはほとんどありません。 IntDoubleのどちらですか? IntメソッドはDoubleメソッドに委任するので、私の推測では、Doubleが「正しい」メソッドであると思います。それを使うだけです。文字通りのオーバーロードのため、次のように呼び出すことができます。

findVal whatever 42

そしてその 42Doubleとして扱われます。これが機能しない唯一のケースは、基本的にIntである何かを取得し、それをこの引数として渡す必要がある場合です。次に、fromIntegralを使用します。しかし、コードでどこでも「正しい」タイプを使用するように努める場合、このケースはまれになります(変換する必要がある場合は、それに注意を払う価値があります)。

6
luqui

この場合、2番目の引数のIntとDoubleの両方を処理する関数を作成するのは簡単だと思います。 findValと書くだけで、2番目の引数でrealToFracが呼び出されます。これにより、IntDoubleに変換され、Doubleはそのままになります。次に、怠惰な場合は、コンパイラに型を推測させます。

3
augustss

他の多くのプログラミング言語では、同じ名前であるが、異なるパラメータータイプなど、シグネチャ内の他のものが異なる(一種の)関数を宣言できます。これはオーバーロードと呼ばれ、アドホック多相性を実現するための最も一般的な方法です。

Haskellは、設計者がアドホック多相を実現するための最良の方法とは考えていないため、意図的にオーバーロードをサポートしていません。 Haskellの方法はむしろ制約されたポリモーフィズムであり、型クラスとクラスインスタンスの宣言が含まれます

0