6

type の関数を取得するために、(Floating a) => a -> a -> atype の関数を使用してtype の関数を構成しようとしています。次のコードがあります。(Floating a) => a -> a(Floating a) => a -> a -> a

test1 :: (Floating a) => a -> a -> a
test1 x y = x

test2 :: (Floating a) => a -> a
test2 x = x

testBoth :: (Floating a) => a -> a -> a
testBoth = test2 . test1
--testBoth x y = test2 (test1 x y)

ただし、GHCI でコンパイルすると、次のエラーが発生します。

/path/test.hs:8:11:
    Could not deduce (Floating (a -> a)) from the context (Floating a)
      arising from a use of `test2'
                   at /path/test.hs:8:11-15
    Possible fix:
      add (Floating (a -> a)) to the context of
        the type signature for `testBoth'
      or add an instance declaration for (Floating (a -> a))
    In the first argument of `(.)', namely `test2'
    In the expression: test2 . test1
    In the definition of `testBoth': testBoth = test2 . test1
Failed, modules loaded: none.

コメントアウトされたバージョンのtestBothコンパイルが実行されることに注意してください。(Floating a)奇妙なことに、すべての型シグネチャから制約を削除するか、 andの代わりにtest1take だけに変更すると、コンパイルされます。xxytestBoth

StackOverflow、Haskell wiki、Google などを検索しましたが、この特定の状況に関連する関数構成の制限については何も見つかりませんでした。なぜこれが起こっているのか誰にも分かりますか?

4

3 に答える 3

17
   \x y -> test2 (test1 x y)
== \x y -> test2 ((test1 x) y)
== \x y -> (test2 . (test1 x)) y
== \x -> test2 . (test1 x)
== \x -> (test2 .) (test1 x)
== \x -> ((test2 .) . test1) x
== (test2 .) . test1

これらの 2 つのことは互いに似ていません。

   test2 . test1
== \x -> (test2 . test1) x
== \x -> test2 (test1 x)
== \x y -> (test2 (test1 x)) y
== \x y -> test2 (test1 x) y
于 2010-12-02T23:34:35.160 に答える
2

あなたの問題は とは何の関係もありませんがFloating、2 つの引数を持つ関数と 1 つの引数を持つ関数をタイプチェックしない方法で作成したいという事実に関係しています。構成された関数の観点から例を挙げますreverse . foldr (:) []

reverse . foldr (:) []タイプが[a] -> [a]あり、期待どおりに機能します。逆のリストを返します(foldr (:) []本質的idにリスト用です)。

ただしreverse . foldr (:)、型チェックは行いません。なんで?

関数合成の型が一致する場合

いくつかのタイプを確認しましょう。

reverse      :: [a] -> [a]
foldr (:)    :: [a] -> [a] -> [a]
foldr (:) [] :: [a] -> [a]
(.)          :: (b -> c) -> (a -> b) -> a -> c

reverse . foldr (:) [](.)次のようにインスタンス化するため、型チェックします。

(.) :: ([a] -> [a]) -> ([a] -> [a]) -> [a] -> [a]

つまり、 の型注釈では次のようになり(.)ます。

  • aになる[a]
  • bになる[a]
  • cになる[a]

reverse . foldr (:) []タイプもそう[a] -> [a]です。

関数合成で型が一致しない場合

reverse . foldr (:)ただし、次の理由により、チェックを入力しません。

foldr (:) :: [a] -> [a] -> [a]

の正しいオペラントである(.)ため、その型を からa -> bにインスタンス化します[a] -> ([a] -> [a])。つまり、次のとおりです。

(b -> c) -> (a -> b) -> a -> c
  • タイプ変数aは次のように置き換えられます[a]
  • タイプ変数bは に置き換えられ[a] -> [a]ます。

のタイプが の場合foldr (:)a -> bのタイプは次の(. foldr (:))ようになります。

(b -> c) -> a -> c`

(foldr (:)への右オペランドとして適用されます(.))。

しかし、 の型は であるためfoldr (:)[a] -> ([a] -> [a])の型(. foldr (:)) は次のとおりです。

(([a] -> [a]) -> c) -> [a] -> c

reverse . foldr (:)reverseタイプがであるため、チェックしませ[a] -> [a]([a] -> [a]) -> c

フクロウのオペレーター

人々が Haskell で関数合成を初めて学ぶとき、関数本体の右端に関数の最後の引数がある場合、引数と本体の両方から削除して、または括弧 (またはドル記号) を置き換えることができることを学びます。 ) ドット付き。つまり、以下の 4 つの関数定義は同等です。

f a x xs = g ( h a ( i x   xs))
f a x xs = g $ h a $ i x   xs
f a x xs = g . h a . i x $ xs
f a x    = g . h a . i x

したがって、人々は「本体と引数から最も右のローカル変数を削除するだけだ」という直感を得るが、この直感は誤りであるxs

f a x = g . h a . i x
f a   = g . h a . i

同等ではありません! 関数合成がいつ型チェックを行い、いつ型チェックを行わないかを理解する必要があります。上記の 2 つが同等である場合、以下の 2 つも同等であることを意味します。

f a x xs = g . h a . i x $ xs
f a x xs = g . h a . i $ x xs

はパラメーターとしてのx関数ではないため、意味がありません。は function のパラメーターであり、は function のパラメーターです。xsxixs(i x)

2つのパラメーターを持つ関数をポイントフリーにするトリックがあります。それは、「フクロウ」演算子を使用することです。

f a x xs = g . h a .  i x xs
f a      = g . h a .: i
  where (.:) = (.).(.)

上記の 2 つの関数定義は同等です。「フクロウ」オペレーターの詳細をお読みください。

参考文献

Haskell プログラミングは、関数、型、部分適用とカリー化、関数合成、およびドル演算子を理解すると、はるかに簡単で単純になります。これらの概念を理解するには、次の StackOverflow の回答をお読みください。

また読む:

于 2015-09-02T15:18:39.707 に答える
2

問題は とは何の関係もありませんがFloating、型クラスによってエラーが理解しにくくなります。以下のコードを例に取ります。

test1 :: Int -> Char -> Int
test1 = undefined

test2 :: Int -> Int
test2 x = undefined

testBoth = test2 . test1

testBoth の型は何ですか? の型を取り、(.) :: (b -> c) -> (a -> b) -> a -> cクランクを回して次のようにします。

  1. b ~ Int( の引数はtest2の最初の引数と統合されます(.))
  2. c ~ Int( のtest2第一引数の結果と統一した結果(.))
  3. a ~ Int(test1の引数 2 と統一された引数 1 (.))
  4. b ~ Char -> Int( のtest1引数 2 で統一した結果(.))

ちょっと待って!その型変数 'b' (#4, ) は、引数の型(#1, )Char -> Intと統一する必要があります。大野!test2Int

これをどのように行う必要がありますか?正しい解決策は次のとおりです。

testBoth x = test2 . test1 x

他にも方法はありますが、これが一番読みやすいと思います。

編集:それで、あなたに伝えようとしていたエラーは何でしたか? Floating a => a -> aと統合するには ...Floating b => bが必要だと言っていましたが、それはinstance Floating (a -> a)本当ですが、GHC に関数を浮動小数点数として扱わせたくありませんでした。

于 2010-12-02T23:39:48.520 に答える