4

リフトされた関数が 2 タプルに適用されると、2 番目のエントリにのみ適用される理由についての説明はありますか?

f x = x + 1
f <$> (2,2)
    // -> (2,3)

一方、2 以外の長さのタプルはエラーを返します。また

:t f <$>

エラーを返します。f <$>タプルに作用するときの型を見ることは可能ですか?

その行動について何か説明はありますか?

Data.Tuple のドキュメントは非常に簡潔で、関数がどのようにタプルに持ち上げられるかについては言及されていません。それを説明するソースはありますか?


アップデート。2-タプルに関する質問の一部はこの回答に関連していますが、複数の長さのタプルに関する上記の質問は扱われていません。

4

4 に答える 4

3

トリプルとより大きなタプルの Functor インスタンスを定義することができます (おそらく、GHC は定義する必要があります)。ウィット:

instance Functor ((,,) a b) where
    fmap f (a, b, c) = (a, b, f c)

この実例が実際に のどこにも存在しない場合base、それはほとんど見落としであると思われますが、確かなことを言えるほど歴史をよく知りません。これは、有用と思われる任意のコードに含めることができますが、このインスタンスは の将来のバージョンに合理的に含まれる可能性があるため、ファイル内の のバージョンにかなり厳密な上限を設定する必要があることに注意してください。このような場合、PVPではバージョンの 3 番目のコンポーネントのみを変更できるため、少なくともその数のコンポーネントを上限に含めてください。base*.cabalbase

于 2016-12-26T09:41:17.893 に答える
3

持ち上げられた関数が 2 タプルに適用されると、2 番目のエントリにのみ適用される理由について説明はありますか?

タプルは異種b -> cであるため、一般に、 typeのタプルの各コンポーネントにtype の関数を適用しようとしても意味がありません(a, b)

同じ型の値のペアが必要な場合は、独自の型Pairを宣言してから、ファンクター インスタンスに関数を各コンポーネントに適用させることができます。

data Pair a = Pair { fst :: a
                   , snd :: a }

instance Functor Pair where
  fmap f (Pair fst snd) = Pair (f fst) (f snd)

f <$>タプルに作用するときの型を見ることは可能ですか?

f <$>セクション(部分的に適用された中置演算子) です。その型を取得するには、次のように括弧で囲む必要があります。

:t (f <$>)

Data.Tuple のドキュメントは非常に簡潔で、関数がどのようにタプルに持ち上げられるかについては言及されていません。それを説明するソースはありますか?

コンビネータ(<$>)(および(<*>)) は、単なるタプルよりも一般的なものであり、Control.Applicativeモジュールで見つけることができます。

于 2016-12-26T09:49:07.233 に答える
2

この回答では、コメントで行った提案の 1 つを少し拡張します。

f <$>タプルに作用するときの型を見ることは可能ですか?

(<$>)は多相関数です。

GHCi> :t (<$>)
(<$>) :: Functor f => (a -> b) -> f a -> f b

GHC 8 では、拡張機能を使用してTypeApplications、型変数の一部またはすべてのインスタンス化を提供することにより、多相関数を特殊化できます (この場合、f、 、aおよびがこのb順序で提供されます)。

GHCi> :set -XTypeApplications 
GHCi> :t (<$>) @Maybe
(<$>) @Maybe :: (a -> b) -> Maybe a -> Maybe b
GHCi> :t (<$>) @Maybe @Int
(<$>) @Maybe @Int :: (Int -> b) -> Maybe Int -> Maybe b
GHCi> :t (<$>) @Maybe @_ @Bool
(<$>) @Maybe @_ @Bool :: (t -> Bool) -> Maybe t -> Maybe Bool
GHCi> :t (<$>) @_ @Int @Bool
(<$>) @_ @Int @Bool
  :: Functor t => (Int -> Bool) -> t Int -> t Bool
GHCi> :t (<$>) @Maybe @Int @Bool
(<$>) @Maybe @Int @Bool :: (Int -> Bool) -> Maybe Int -> Maybe Bool

ペアでそれを使用するには、ペア型コンストラクターのプレフィックス構文を使用します。

GHCi> :t (<$>) @((,) _)
(<$>) @((,) _) :: (a -> b) -> (t, a) -> (t, b)
GHCi> -- You can use the specialised function normally.
GHCi> -- That includes passing arguments to it.
GHCi> f x = x + 1
GHCi> :t (<$>) @((,) _) f
(<$>) @((,) _) f :: Num b => (t, b) -> (t, b)

_inは、ペアの最初の要素 (型コンストラクター((,) _)の最初の引数) の型を未指定のままにします。(,)それを選択するたびに、異なるFunctor. 必要に応じて、より具体的に指定できます。

GHCi> :t (<$>) @((,) String) f
(<$>) @((,) String) f :: Num b => (String, b) -> (String, b)

最後に、3 タプルでこれを試してみるとどうなるか見てみる価値があります。

GHCi> :t (<$>) @((,,) _ _) f
(<$>) @((,,) _ _) f
  :: (Num b, Functor ((,,) t t1)) => (t, t1, b) -> (t, t1, b)

Daniel Wagner がanswer で説明しているように、baseFunctorは3 タプルのインスタンスを定義しません。それにもかかわらず、型チェッカーは、どこかで誰かが最初の 2 つの型パラメーターの選択に固有のインスタンスを定義した可能性を排除することはできませんが、それは無意味です。そのため、投機的制約が型に現れます ( baseにインスタンスFunctor ((,,) t t1)があるため、ペアではそのようなことは起こりません)。予想通り、最初の 2 つの型パラメーターをインスタンス化しようとするとすぐに爆発します。Functor ((,) a)

GHCi> :t (<$>) @((,,) Bool String) f

<interactive>:1:1: error:
    • Could not deduce (Functor ((,,) Bool String))
        arising from a use of ‘&lt;$>’
      from the context: Num b
        bound by the inferred type of
                 it :: Num b => (Bool, String, b) -> (Bool, String, b)
        at <interactive>:1:1
    • In the expression: (<$>) @((,,) Bool String) f
于 2016-12-26T18:06:16.920 に答える