Rustで圏論(2) マクロで独自記法

【要訂正】同型を判断したい

前回は圏を構造化したところまででした。というわけで、そこから様々なことを調べられるようにしたい。 手始めに、同型かどうかを調べる関数を追加した(関数名がダサいのは気にしないでください)。 しかし、実はこの関数の中身は大きく間違ってるんですけど、お分かりでしょうか。 修正したのは次回、出します。

独自記法をマクロで書く

最初はCategory::newを普通に作って書いてたんですが、面倒なのでマクロで簡単に書けるようにした。 こういうのがあっさりできるの、ほんまRust最高である。 マクロ展開のデバッグには、trace_macros!(true) trace_macros!(false)が便利でした。 事前に#![feature(trace_macros)]をお忘れなく。

結果、以下のように書けるようになりました。

    let category1 = cat!(
        [A B C]
        [f: A -> B
         g: A -> C
         h: B -> C]
    );

全コード

そのうち全部は載せられなくなりそうですけど、その時はまた考えます。

macro_rules! cat {
    ([ $( $object:ident )* ]
     [ $( $arrow_name:ident : $domain:ident -> $codomain:ident )* ]) => ({
        let mut objects = Vec::new();
        $( objects.push(stringify!($object)); )*
        let mut arrows = Vec::new();
        $( arrows.push((
            stringify!($arrow_name), 
            stringify!($domain), 
            stringify!($codomain))); )*
        new_category(objects, arrows)
    })
}

use std::fmt::Display;
use std::collections::HashMap;

type Arrows = HashMap<String, (String, String)>;

struct Category {
    objects: Vec<String>,
    arrows: Arrows
}

impl Category {
    fn has_arrow(&self, domain: &str, codomain: &str) -> bool {
        for (dom, cod) in self.arrows.values() {
            if dom == domain && cod == codomain {
                return true
            }
        }
        false
    }

    fn is_isomorphism(&self, object1: &str, object2: &str) -> bool {
        self.has_arrow(object1, object2) && self.has_arrow(object2, object1)
    }
}

impl Display for Category {
    fn fmt(&self, dest: &mut std::fmt::Formatter) -> std::fmt::Result {
        let mut arrow_texts = vec![];
        for (k, v) in &self.arrows {
            arrow_texts.push(format!("{}: {} -> {}", k, v.0, v.1));
        }
        write!(dest, "objects: {:?} \n arrows: {:?}", self.objects, arrow_texts)
    }
}

fn insert_arrow(arrows: &mut Arrows, name: &str, domain: &str, codomain: &str) {
    arrows.insert(name.to_string(), (domain.to_string(), codomain.to_string()));
}

fn check_isomorphism(category: Category, object1: &str, object2: &str) {
    println!("{} and {} are isomorphism: {:?}",
        object1, object2, category.is_isomorphism(object1, object2));
}

fn new_category(objects: Vec<&str>, arrow_names: Vec<(&str, &str, &str)>) -> Category {
    let mut arrows = HashMap::new();
    for arrow in &arrow_names {
        insert_arrow(&mut arrows, arrow.0, arrow.1, arrow.2);
    }
    let category = Category {
        objects: objects.iter().map(|s| s.to_string()).collect(), 
        arrows: arrows
    };
    println!("made {}", category);
    category
}

fn main() {
    let category1 = cat!(
        [A B C]
        [f: A -> B
         g: A -> C
         h: B -> C]
    );
    check_isomorphism(category1, "A", "B");

    let category2 = cat!(
        [A B]
        [f: A -> B
         g: B -> A]
    );
    check_isomorphism(category2, "A", "B");
}