私はHaskellを初めて使用します。ここで問題が発生し<*>
ます:
((==) <*>) :: Eq a => (a -> a) -> a -> Bool
これをどのように理解し、どのように推測できますか?
私はHaskellを初めて使用します。ここで問題が発生し<*>
ます:
((==) <*>) :: Eq a => (a -> a) -> a -> Bool
これをどのように理解し、どのように推測できますか?
免責事項: これは慣用的な Haskell コードではありません。
最初に優先されるのは、 の「演算子セクション」です<*>
。セクションと呼ばれる 1 つの引数にのみ演算子が適用されている場合。より一般的な演算子セクションの例を次に示します。
(1 +) :: Int -> Int
これは 1 に部分的に適用される関数であり、+
最後の引数を 1 つ残す余地があります。それは以下と同等です:
\x -> 1 + x
したがって、コード サンプルで<*>
は が に部分的に適用されて(==)
いるため、次のように展開します。
((==) <*>)
= \g -> (==) <*> g
次に、 が何をしているのかを理解する必要があります<*>
。これはApplicative
型クラスのメンバーです:
class (Functor f) => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
これは、<*>
を実装する任意の型で機能するようにオーバーロードされていることを意味しますApplicative
。Applicative
タイプの 1 つのインスタンスは次の((->) r)
とおりです。
instance Applicative ((->) r) where
pure :: a -> ((->) r) a
(<*>) :: (->) r (a -> b) -> (->) r a -> (->) r b
プレフィックス形式で使用されている意味を囲む括弧(->)
(これは、このようなクラス インスタンスを定義するときに構文上の理由で必要です)。それを中置形式に展開すると、次のようになります。
instance Applicative ((->) r) where
pure :: a -> r -> a
(<*>) :: (r -> a -> b) -> (r -> a) -> (r -> b)
あなたの特定の例では、の最初の引数は<*>
次(==)
の型を持つ演算子です。
(==) :: Eq e => e -> e -> Bool
したがって、これをコンパイラに渡すと、 、、およびのシグネチャ(<*>)
の型について、次のように推測できます。r
a
b
(<*>)
(<*>) :: (r -> a -> b ) -> (r -> a) -> (r -> b)
(==) :: Eq e => e -> e -> Bool
| | |
| | +-> `b` must be `Bool`
| |
| +------> `a` must be `e`
|
+-----------> `r` must also be `e`
したがって(==)
、 の最初の引数として指定すると、次の(<*>)
推論された型が得られます。
((==) <*>) :: Eq e => (e -> e) -> (e -> Bool)
(->)
は右結合であるため、右括弧を省略し、 に変更e
しa
て、取得した最終的な署名を与えることができます。
((==) <*>) :: Eq a => (a -> a) -> a -> Bool
しかし、これは実際に何をしているのでしょうか?のインスタンス(<*>)
に対して がどのように定義されているかを確認する必要があることを理解するには、次のようにします。Applicative
((->) r)
(f <*> g) r = f r (g r)
と置き換えるf
と、次の(==)
ようになります。
((==) <*> g) r = (==) r (g r)
(==)
括弧で囲む場合、それは接頭表記で使用していることを意味します。これは、括弧を削除して中置記法に戻すと、次のようになることを意味します。
((==) <*> g) r = r == (g r)
これは次と同等です。
((==) <*>) = \g r -> r == g r
つまり、関数は と の 2 つのパラメーターを取りg
、r
if r
equalsを確認しg r
ます。