whereを使用したネストされた関数定義のHaskellスコープ
-
10-07-2019 - |
質問
where
の定義におけるHaskellのスコープに問題があります。次の関数 f
があり、明示的にパラメーターとして使用せずに、ローカルで定義された関数 f1
に x
を渡したい場合、 x
の型は f1
の出力にある型と互換性がないというエラーが表示されますが、同じである必要があります:
f :: Eq a => a -> [a] f x = f1 x where f1 :: Eq a => a -> [a] f1 y = [ x, y ]
エラーは次のとおりです。
Couldn't match expected type `a1' against inferred type `a' `a1' is a rigid type variable bound by the type signature for `f1' at test.hs:4:11 `a' is a rigid type variable bound by the type signature for `f' at test.hs:1:8 In the expression: x In the expression: [x, y] In the definition of `f1': f1 y = [x, y] Failed, modules loaded: none.
ただし、次のコードで関数 g
を使用して行ったように、追加パラメーターとして x
を渡すと、正常に動作します。
g :: Eq a => a -> [a] g x = g1 x x where g1 :: Eq a => a -> a -> [a] g1 x y = [ x, y ]
f
の a
型を a
型(または a1
型と互換性を持たせる方法はありますか? ) f1
?
解決
コードの問題は、ローカルスコープのf1型署名です。 f1が任意の型を取ることができることを指定します
f1 :: Eq a => a-> [a]
これはローカル関数ですが、この関数はf内に存在しない型を取得できるように一般化しました。この関数はfからHASを受け取るため、型シグネチャは不要です。
f1タイプの署名を削除するだけです。
編集:自分の投稿を読み直してください。少し不明瞭です。 f1のaは何でも取ることができるパラメータ化された型ですが、それに渡される引数は既にfにバインドされています。そのため、この関数は親関数が受け取るもののみを受け取ることができ、与えている型シグネチャはその規則を破ります。それがもう少し明確であることを願っています。
他のヒント
デイブは真上です。別の考え方としては、両方の型シグネチャが変数 a
を参照していても、実際には同じ型変数ではないということです。 Haskell-prime表記では、両方の署名を次のように明示的に記述できます。
forall a eq a => a-> [a]
は、両方関数について、(Eq内で)あらゆるタイプの引数を受け入れることができることを意味します。ここでは明らかにそうではありません。標準のHaskell 98では、唯一のオプションは f1
の型シグネチャを放棄することです。ただし、GHC(および他のユーザー)は字句的にサポートしています。スコープ付きタイプ変数。だからあなたは書くことができます
{-# LANGUAGE ScopedTypeVariables #-}
f :: forall a. Eq a => a -> [a]
f x = f1 x
where
f1 :: a -> [a]
f1 y = [ x, y ]
それで問題なく動作します。