このブログの更新は Twitterアカウント @m_hiyama で通知されます。
Follow @m_hiyama

メールでのご連絡は hiyama{at}chimaira{dot}org まで。

はじめてのメールはスパムと判定されることがあります。最初は、信頼されているドメインから差し障りのない文面を送っていただけると、スパムと判定されにくいと思います。

[参照用 記事]

自然変換としてのzipをCatyScriptで書いてみた

昨日の「zipが自然変換だという話」において、zipの自然性は次の可換図式で表現できると述べました。

PLL(A, B) - zip_(A,B) → LP(A, B)
|                       |
PLL(f, g)               LP(f, g)
↓                       ↓
PLL(C, D) - zip_(C,D) → LP(C, D)

ここで、PLLとLPは、C×C→C という関手です。A, B, C, D は圏Cのすべての対象に渡って動くことになります。また、f:A→C、g:B→D もあらゆる射に対して変化させます。全称記号を使った論理式で書くなら:

  • ∀A, B, C, D∈|C|.∀f∈C(A, C), g∈C(B, D). PLL(f, g);zipC,D = zipA,B;LP(f, g)

です。写像の等式 h = k は、∀z∈Z. h(z) = k(z) の形なので、それも展開すると:

  • ∀A, B, C, D∈|C|.∀f∈C(A, C), g∈C(B, D).∀z∈PLL(A, B). zipC,D(PLL(f, g)(z)) = LP(f, g)(zipA,B(z))

エライこと全称記号が続いた論理式になります。「すべての…のすべての…のすべての…の」といった意味なので、1個や2個の事例について確認しても何の意味もありません。とはいえ、1個か2個の事例でも確認してみたいものです。

CatyScriptは明示的な型付けを書くのが割と得意なので、完全に型付けしたzipを書いて、それが自然変換であることを zip-is-natural という述語で表現します。

/** テストサンプル */
command test-sample :: void -> Logical {

 [ [true, false, false, true], [4, 5, 6, 7] ]
 |
 zip-is-natural "to-string" "num:inc"

};

この事例では、4つの対象A, B, C, Dには次のように型(集合)が代入されます。

  • A = boolean, B = integer, C = string, D = number

f:A→C は to-string : boolean→string、g:B→D としては num:inc : integer→number(数に1を足す)を使います。実際に試すデータは、PLL(boolean, string) の要素である [ [true, false, false, true], [4, 5, 6, 7] ] です。

caty:test> test-sample
@True [
    [
        [
            "true",
            5
        ],
        [
            "false",
            6
        ],
        [
            "false",
            7
        ],
        [
            "true",
            8
        ]
    ],
    [
        [
            "true",
            5
        ],
        [
            "false",
            6
        ],
        [
            "false",
            7
        ],
        [
            "true",
            8
        ]
    ]
]

自然変換としてのzipの定義はこの下にあります。

/** 射を引数にするためのナンチャッテ定義
 */
type Code<X, Y> = string(remark="X -> Y のコードを表す文字列");

/** リスト関手
 */
type  List<A default any> = [A*];
class List = {
  command fmap<A default any, B default any>
    [Code<A, B> code]
  :: List<A> -> List<B> 
  {
    each {[pass, %1] | eval}
  };
};

/* ペアリング関手
 */
type  Pair<A default any, B default any> = [A, B];
class Pair = {
  command fmap<A default any, B default any, C default any, D default any> 
    [Code<A, C> code1, Code<B, D> code2]
  :: Pair<A, B> ->Pair<C, D> 
  {
    =[
      [pass, %1] | eval, 
      [pass, %2] | eval,
    ]
  };
};

/* PLL = Pair(List(-), List(-)) 関手
 */
type  PLL<A default any, B default any> = Pair<List<A>, List<B>>;
class PLL = {
  command fmap<A default any, B default any, C default any, D default any>
    [Code<A, C> code1, Code<B, D> code2]
  :: PLL<A, B> ->PLL<C, D> 
  {
    =[
      each {[pass, %1] | eval}, 
      each {[pass, %2] | eval},
    ]
  };
};

/* LP = List(Pair(-, -)) 関手
 */
type  LP<A default any, B default any> = List<Pair<A, B>>;
class LP = {
  command fmap<A default any, B default any, C default any, D default any>
    [Code<A, C> code1, Code<B, D> code2]
  :: LP<A, B> ->LP<C, D> 
  {
    each { 
      =[
         [pass, %1] | eval, 
         [pass, %2] | eval,
      ] 
    }
  };
};

/** 自然変換としてのzip
 */
command zip<A default any, B default any> 
:: PLL<A, B> -> LP<A, B>
{
  list:zip
};

/** zipの自然性  
 */
command zip-is-natural<A default any, B default any, C default any, D default any> 
  [Code<A, C> code1, Code<B, D> code2]
:: PLL<A, B> -> Logical 
{
  [
    PLL.fmap<A, B, C, D> %1 %2 | zip<C, D> ,
    zip<A, B> | LP.fmap<A, B, C, D> %1 %2 ,
  ] | eq
};

/** テストサンプル */
command test-sample :: void -> Logical {

 [ [true, false, false, true], [4, 5, 6, 7] ]
 |
 zip-is-natural<boolean, integer, string, number> "to-string" "num:inc"

};