タイプ[[a]]の法則->([a]、[a])


8

私は宿題からこの質問をしようとしています:

任意の場合、リストとペアを含めfoo :: [[a]] -> ([a], [a])、関数がfoo満たす1つの法則を書き留めmapます。

いくつかのコンテキスト:私は関数型プログラミングのコースを受講する1年生です。コースはかなり入門的なものですが、講師はシラバスから多くのことを述べてきましたが、その中には自由定理があります。だから、Wadlerの論文を読むことを試みた後、私はそれを起算concat :: [[a]] -> [a]法律をmap f . concat = concat . map (map f)我々は持っている必要がありますから、私の問題に関連して見えるfoo xss = (concat xss, concat' xss)場所concatconcat'種類のいずれかの機能です[[a]] -> [a]。次にをfoo満たしbimap (map f, map g) . foo = \xss -> ((fst . foo . map (map f)) xss, (snd . foo . map (map g)) xss)ます。

すでにこの「法則」は長すぎて正しくないように思われますが、自分の論理もよくわかりません。だから私はオンラインの無料の定理ジェネレーターを使うことを考えましたが、私は何をlift{(,)}意味するのかわかりません:

forall t1,t2 in TYPES, g :: t1 -> t2.
 forall x :: [[t1]].
  (f x, f (map (map g) x)) in lift{(,)}(map g,map g)

lift{(,)}(map g,map g)
  = {((x1, x2), (y1, y2)) | (map g x1 = y1) && (map g x2 = y2)}

この出力をどのように理解すればよいですか?そして、関数の法則をfoo適切に導出するにはどうすればよいですか?


5
これは次のように言っていると思います(\(a,b) -> (map f a, map f b)) . foo = foo . map (map f)
AJFarmar

回答:


4

場合R1R2の関係(と言っているR_iA_iB_i、とi in {1,2})、その後lift{(,)}(R1,R2)、関係のペアを「解除」される間A1 * A2B1 * B2、と*(書かれた製品表す(,)Haskellでの)。

存続関係では、2つのペア(x1,x2) :: A1*A2、およびの(y1,y2) :: B1*B2場合にのみ関連x1 R1 y1x2 R2 y2ます。あなたの場合、R1そしてR2は関数なmap g, map gので、リフティングも関数になります:y1 = map g x1 && y2 = map g x2

したがって、生成された

(f x, f (map (map g) x)) in lift{(,)}(map g,map g)

手段:

fst (f (map (map g) x)) = map g (fst (f x))
AND
snd (f (map (map g) x)) = map g (snd (f x))

または、言い換えると:

f (map (map g) x) = (map g (fst (f x)), map g (snd (f x)))

私は次のように書いて使用しましたControl.Arrow

f (map (map g) x) = (map g *** map g) (f x)

または、ポイントフリースタイル:

f . map (map g) = (map g *** map g) . f

あなたのfように書くことができるので、これは驚きではありません

f :: F a -> G a
where F a = [[a]]
      G a = ([a], [a])

そしてFGファンクタ(Haskellで私たちが使用する必要がありますされているnewtype数子のインスタンスを定義するために、それは無関係なので、私は、それを省略します)。このような一般的なケースでは、自由定理は非常に優れた形式を持っていますg

f . fmap_of_F g = fmap_of_G g . f

これは自然と呼ばれる非常に素晴らしい形式です(f適切なカテゴリの自然な変換として解釈できます)。f上記の2つのは実際には別々の型でインスタンス化されるため、型を残りの型と一致させるために注意してください。

あなたの特定のケースではF a = [[a]]、それは[]それ自体でファンクターの構成なので、私たちは(当然のことながら)を取得しfmap_of_F g = fmap_of_[] (fmap_of_[] g) = map (map g)ます。

代わりに、G a = ([a],[a])ファンクタ[]H a = (a,a)(技術的には、製品ファンクタで構成された対角ファンクタ)の構成です。我々は持ってfmap_of_H h = (h *** h) = (\x -> (h x, h x))いるから、fmap_of_G g = fmap_of_H (fmap_of_[] g) = (map g *** map g)


いい説明だ!ただの質問です。「すべてのgに対して」と言うとき、gは合計または厳密である必要がありますか、それともまったく制限がありませんか?
Jingjie YANG

1
@JingjieYANGはい、Haskellを使用する場合、いくつかの制限があります。このような結果のほとんどは、すべてが終了する純粋な型システムで実際に行われます(したがって、それは合計です)。Haskellでは、私が正しく思い出せば、終了しないため、g合計を要求する必要があります。同様に、私たちは厳格であることseqを要求gする必要があるからです。正確な制限については100%確信が持てませんが、これで十分でしょう。しかし、それらについてどこで読んだか覚えていませんが、おそらく無料の定理ジェネレーターのページにいくつかの情報があります。
チー

Data.Bifunctor(bimap)を支持して、タプルのControl.Arrow(***)は少しスタイルが狂っていませんか?後者に変更するための編集に対する異議はありますか?
Joseph Sible-Reinstate Monica

2
@ JosephSible-ReinstateMonicaわかりません。それはmapvsに少し似ていると思いfmapます。map私たちがリストを扱っていることは明らかであるため(他のファンクターではなく)、人々は使い続けています。同様に、(***)ペアでのみ機能します(他のバイファンクターでは機能しません)。数学では私たちはf \times g積和関数を適用するように書く傾向があるので、私はおそらくそのほとんどをその固定性のために使用しています。たぶん、のバリアントのbimapように、そのインフィックスバリアントも必要<$>ですfmap
チー

1
本当だが(***)より具体的であるbimapことが唯一のペアではなく、任意のbifunctorsに動作することで、それはそれも事実だbimapより具体的である(***)ことが唯一の機能ではなく、任意の矢印の上に動作することでは。中置再、それがために全く同じではないであろうbimapfmapするので、bimap3つのパラメータを受け取り、fmap唯一の2取る
ジョセフSible-復活モニカの

2

セレモニーが少ない@chiの回答と同じです:

関数の前または後にasをbsに変更しても、同じ結果になります(fmap-like-thingを使用する限り)。

任意のfの場合:a-> b、

    [[a]] -------------> [[b]]
      | (map.map)f |
      | |
     foo foo
      | |
      vv
    ([a]、[a])--------->([b]、[b])
              バイマップff

通勤。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.