Haskell:2つの浮動的な引数で機能する機能は失敗します
-
30-09-2019 - |
質問
タイプの関数を作成しようとしています (Floating a) => a -> a -> a
タイプの関数付き (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)
すべてのタイプの署名からの制約、または変更した場合 test1
ただ取る x
それ以外の x
と y
, testBoth
コンパイル。
Stackoverflow、Haskell Wikis、Googleなどを検索しましたが、この特定の状況に関連する機能構成の制限については何も見つかりませんでした。なぜこれが起こっているのか誰もが知っていますか?
解決
\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
他のヒント
あなたの問題は何の関係もありません Floating
, 、Typeclassはあなたのエラーを理解しにくくしますが。以下のコードを例として取ります:
test1 :: Int -> Char -> Int
test1 = undefined
test2 :: Int -> Int
test2 x = undefined
testBoth = test2 . test1
テストの種類は何ですか?さて、私たちはタイプを取ります (.) :: (b -> c) -> (a -> b) -> a -> c
クランクを回して取得します。
b ~ Int
(の議論test2
の最初の議論と統一された(.)
)c ~ Int
(結果としてtest2
の最初の議論の結果と統一された(.)
)a ~ Int
(test1
引数1の引数2が統一されています(.)
)b ~ Char -> Int
(の結果test1
の引数2で統一されました(.)
)
ちょっと待って!そのタイプ変数、 'b'(#4、 Char -> Int
)、引数タイプのタイプで統一する必要があります test2
(#1, Int
)。大野!
これをどうすればいいですか?正しい解決策は次のとおりです。
testBoth x = test2 . test1 x
他の方法はありますが、これは最も読みやすいと思います。
編集:それで、あなたに伝えようとしているエラーは何でしたか?それは統一していると言っていました Floating a => a -> a
と Floating b => b
必要があります instance Floating (a -> a)
...それは本当ですが、あなたは本当にGHCに機能を浮動小数点数として扱おうとしたくありませんでした。
あなたの問題は何の関係もありません Floating
, 、しかし、TypeCheckではない方法で、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 (:) []
TypeChecks、なぜなら (.)
インスタンス化:
(.) :: ([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で関数の構成を学ぶとき、彼らはあなたが関数本体の右に関数の最後の引数を持っているとき、あなたはそれを議論と身体から、置き換えまたは括弧(またはDollar-Signsの両方から落とすことができることを学びます)ドット付き。言い換えれば、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
機能ではありません xs
パラメーターとして。 x
機能するパラメーターです i
, 、 と xs
機能するパラメーターです (i x)
.
機能を作成するトリックがあります 2 ポイントフリーのパラメーター。そして、それは「フクロウ」演算子を使用することです。
f a x xs = g . h a . i x xs
f a = g . h a .: i
where (.:) = (.).(.)
上記の2つの関数定義は同等です。読む 「Owl」オペレーターの詳細.
参照
Haskellプログラミングは、関数、タイプ、部分的なアプリケーションとカレー、関数構成、ドルオペレーターを理解すると、はるかに簡単かつ簡単になります。これらの概念を釘付けにするには、次のStackOverFlowの回答を読んでください。
- の上 タイプと関数構成
- の上 高次関数、カレー、および関数構成
- の上 Haskellタイプシステム
- の上 ポイントフリースタイル
- の上
const
- の上
const
,flip
およびタイプ - の上
curry
とuncurry
また読む: