6

Haskellで次のようなものを書くにはどうすればよいですか:

showSquare :: (Show a, Num a) => a -> String
showSquare x = "The square of " ++ (show x) ++ " is " ++ (show (x * x))

showSquare :: (Show a, not Num a) => a -> String
showSquare x = "I don't know how to square " ++ (show x)

基本的には、C++のboost::enable_ifのようなものです。

GHC 拡張機能は問題ありません。

4

4 に答える 4

6

なぜあなたはこれが欲しいのですか?タイプチェッカーは、最初のケースでshowSquareはないものを呼び出さないようにします。すべてが静的に入力されるため、Haskellにはありません。Numinstanceof

任意の型では機能しません。独自の型クラスのみを定義できます。

class Mine a where
  foo :: a -> String

instance (Num a) => Mine a where
  foo x = show x*x

instance Mine aまた、他のクラスのインスタンスをさらに追加することはできますが、任意のに対してのみ書き込むことはできませんa重複するインスタンスも許可されていないため、追加instance (Show a) => ...も役に立ちません(リンクはそれを回避する方法を説明していますが、かなりの追加の機械が必要です)。

于 2012-08-10T06:45:17.030 に答える
3

私が本当にこのようなものを絶対に必要としていた場合(そして私が今まで持っていたとは思わない)、これはHaskellで最も簡単なアプローチだと思います:

class Show a => ShowSquare a where
  showSquare :: a -> String
  showSquare a = "I don't know how to square " ++ (show a)

instance ShowSquare Int where
  showSquare = showSquare'

instance ShowSquare Double where
  showSquare = showSquare'

-- add other numeric type instances as necessary

-- make an instance for everything else
instance Show a => ShowSquare a

showSquare' :: (Show a, Num a) => a -> String
showSquare' x = "The square of " ++ (show x) ++ " is " ++ (show (x * x))

これには、明らかに重複するインスタンスが必要です。必要な定型文について不満を言う人もいるかもしれませんが、それはごくわずかです。5または6インスタンスは、ほとんどの数値数値タイプをカバーします。

AdvancedOverlapwikiページのアイデアを使用して何かを機能させることができるでしょう。テクニックではインスタンスを明示的にリストする必要があることに注意してください。したがって、これより優れているかどうかはおそらく好みの問題です。

関数の代わりにTHスプライスを書くことで、テンプレートhaskellの問題に取り組むことも可能です。スプライスは、インスタンスがスコープ内にreify ''Numあるかどうかを判断するために呼び出しサイトで行う必要があり、次に適切な関数を選択します。Numただし、これを機能させるには、Numインスタンスを手動で書き出すよりも問題が発生する可能性があります。

于 2012-08-10T12:45:38.787 に答える
3

まず、同じ関数の異なる方程式に異なる型シグネチャを与えることはまったく不可能です。どの関数も、方程式の数に関係なく、1 つの型しか持てません。

第二に、負の制約は Haskell では適切な意味を持ちません (持たないでしょう)。クラス制約の意味を思い出してください。

f :: Num a => a -> a -> a
f x y = x + y

Num atype ofは、型 classfの任意のクラス メソッドをNumtype の値に適用できることを意味しますa。一般的な動作を得るために、意識的に具象型に名前を付けていません。本質的に、私たちは「正確に何であるかは気にしませんが、操作がそれに適用できることaは知っています」と言っています。Numしたがって、Numメソッド onxおよびを使用できますy、それ以上は使用できません。つまり、メソッド onおよび以外は何も使用できません。これが型クラス制約とは何か、なぜ必要なのかです。関数の汎用インターフェイスを指定しています。Numxy

ここで、架空のnot Num a制約を考えてみましょう。この声明はどのような情報をもたらしますか? aそれが であってはならないことはわかっていますNum。しかし、この情報は私たちにとってまったく役に立ちません。検討:

f :: not Num a => a -> a
f = ???

の代わりに何を置けます???か? 明らかに、私たちは配置できないものを知っています。ただし、この署名にはそれ以上の情報がないことを除いて

f :: a -> a

唯一の操作は次のfとおりですid(まあ、undefinedそれも可能ですが、それは別の話です)。

最後にあなたの例を考えてみましょう:

showSquare :: (Show a, not Num a) => a -> String
showSquare x = "I don't know how to square " ++ (show x)

私はあなたの例の最初の部分を意図的に与えていません。私の答えの最初の文を見てください。異なるタイプの異なる方程式を持つことはできません。しかし、この機能だけではまったく役に立ちません。ここで制約を安全に削除できますがnot Num a、何も変更されません。

Int静的に型付けされた Haskell でのこのような負の制約の唯一の使用法は、たとえばfor not Num a-constrainted 変数を指定したときにコンパイル時エラーを生成することです。しかし、これには何の役にも立ちません。

于 2012-08-10T07:07:13.700 に答える
2

「not a Num a」に依存することは、Haskell では非常に脆弱であり、C++ では脆弱ではありません。

C++ では、クラスは 1 つの配置 (クローズ) で定義されますが、Haskell 型クラスはオープンであり、モジュール A のデータとモジュール B のクラスのモジュール C でインスタンスを宣言できます。

型クラスの (拡張なしの) 解決には、"C" のようなモジュールをインポートしても型クラスの以前の解決が決して変更されないという指針があります。

「Num Custom ではない」と予想されるコードは、再帰的にインポートされたモジュール (たとえば、別のパッケージから) が「インスタンス Num Custom」を定義した場合に変更されます。

ポリモーフィズムには別の問題があります。モジュール「D」の関数を考えてみましょう

useSS :: Show a => a -> Int -> [String]
useSS a n = replicate n (showSquare a)

data Custom = Custom deriving Show
use1 :: Int -> String
use1 = useSS Custom -- no Num Custom in scope

上記のモジュール「D」をインポートする別のパッケージのモジュール「E」を考えてみましょう

instance Num Custom
use2 :: Int -> String
use2 = useSS Custom -- has a Num Custom now

(use1 1)(use2 1)評価する必要がありますか?このような罠のある言語で働きたいですか?Haskell は、原則に基づいた設計により、このトラップの存在を防ごうとしています。

この種のアドホックなオーバーロードは、C++ の解決策のいたるところにありますが、まさに Haskell が回避するように設計されたものです。GHC 拡張機能を使用してそのようなことを行うことは可能ですが、危険なトラップを作成しないように注意する必要があり、推奨されません。

于 2012-08-10T13:10:11.580 に答える