3

多変量関数の例を探しているときに、次のリソースを見つけました: StackOverflow: How to create a polyvariadic haskell function? 、そして次のような回答スニペットがありました:

class SumRes r where 
  sumOf :: Integer -> r

instance SumRes Integer where
  sumOf = id

instance (Integral a, SumRes r) => SumRes (a -> r) where
  sumOf x = sumOf . (x +) . toInteger

次に、次を使用できます。

*Main> sumOf 1 :: Integer
1
*Main> sumOf 1 4 7 10 :: Integer
22
*Main> sumOf 1 4 7 10 0 0  :: Integer
22
*Main> sumOf 1 4 7 10 2 5 8 22 :: Integer
59

一見したところかなりトリッキーだとわかったので、好奇心のために少し変更してみましたが、次のようになりました。

class SumRes r where
  sumOf :: Int -> r

instance SumRes Int where
  sumOf = id

instance (SumRes r) => SumRes (Int -> r) where
  sumOf x = sumOf . (x +)

に変更IntegerIntinstance (Integral a, SumRes r) => SumRes (a -> r) where多態性を減らしましたinstance (SumRes r) => SumRes (Int -> r) where

それをコンパイルするには、XFlexibleInstancesフラグを設定する必要がありました。関数をテストしようとするsumOfと、問題が発生しました。

*Main> sumOf 1 :: Int
1
*Main> sumOf 1 1 :: Int
<interactive>:9:9
    No instance for (Num a0) arising from the literal `1'
    The type variable `a0' is ambiguous...

それから私は試しました:

*Main> sumOf (1 :: Int) (1 :: Int) :: Int
2

型クラス内でIntを使用しているのに、なぜ Haskell はこの状況で が必要であると推測できないのでしょうか?IntSumRes

4

2 に答える 2

5

インスタンス

instance (...) => SumRes (Int -> r) where

大まかに言うと、「 (特定の条件下で)SumResonInt -> rを定義する方法は次のとおりです」という意味です。rと比較してください

instance (...) => SumRes (a -> r) where

これは、「 (特定の条件下で) for anyを定義する方法は次のとおりです」という意味SumResです。a -> ra,r

主な違いは、2 番目のものは、これがどのタイプであってもa,r関連するインスタンスであると述べていることです。一部の (非常にトリッキーで潜在的に危険な) Haskell 拡張機能を除いて、後で関数を含むインスタンスを追加することはできません。代わりに、最初のものは、次のような新しいインスタンスのための余地を残します

instance (...) => SumRes (Double -> r) where ...
instance (...) => SumRes (Integer -> r) where ...
instance (...) => SumRes (Float -> r) where ...
instance (...) => SumRes (String -> r) where ... -- nonsense, but allowed

これは、 などの数値リテラルが多相的であるという事実と対になっています5。それらの型はコンテキストから推測する必要があります。後でコンパイラはDouble -> rインスタンスなどを見つけてリテラル型として選択する可能性があるためDouble、コンパイラはインスタンスにコミットせずInt -> r、型エラーであいまいさを報告します。

いくつかの (安全な) Haskell 拡張機能 ( など) を使用すると、プログラム全体で宣言されるのはTypeFamilies自分だけであることをコンパイラーに「約束」できることに注意してください。Int -> rこれは次のように行われます。

instance (..., a ~ Int) => SumRes (a -> r) where ...

これは、すべての「関数型」のケースを処理することを約束しますが、a実際には と同じ型である必要がありIntます。

于 2015-10-25T19:55:52.750 に答える