0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

科学と神々株式会社Advent Calendar 2024

Day 2

~モナドによる関心事分離とパターンの自動適用~

Posted at

第2回:Ohama - 横断的関心事の実装

~モナドによる関心事分離とパターンの自動適用~

はじめに

前回のアーキテクチャ概観を踏まえ、今回はOhamaフレームワークの詳細な実装を解説します。Ohamaフレームワークは以下の特徴を持ちます:

  • モナドベースの関心事分離
  • デザインパターンの自動適用
  • ボイラープレートの削減
  • 透過的な機能追加

モナドベースの関心事分離

// 基本的なモナド型の定義
pub struct Concern<T> {
    value: T,
    context: ConcernContext,
}

impl<T> Concern<T> {
    pub fn new(value: T) -> Self {
        Self {
            value,
            context: ConcernContext::default(),
        }
    }

    pub fn map<U, F>(self, f: F) -> Concern<U>
    where
        F: FnOnce(T) -> U,
    {
        Concern {
            value: f(self.value),
            context: self.context,
        }
    }

    pub fn and_then<U, F>(self, f: F) -> Concern<U>
    where
        F: FnOnce(T) -> Concern<U>,
    {
        let result = f(self.value);
        Concern {
            value: result.value,
            context: self.context.merge(result.context),
        }
    }
}

// 横断的関心事の実装例
pub trait CrossCuttingConcern {
    fn before<T>(&self, value: &T, context: &ConcernContext);
    fn after<T>(&self, value: &T, context: &ConcernContext);
    fn on_error(&self, error: &Error, context: &ConcernContext);
}

// ロギング関心事の実装
pub struct LoggingConcern {
    logger: Logger,
}

impl CrossCuttingConcern for LoggingConcern {
    fn before<T>(&self, value: &T, context: &ConcernContext) {
        self.logger.info(&format!("Starting operation: {:?}", value));
    }

    fn after<T>(&self, value: &T, context: &ConcernContext) {
        self.logger.info(&format!("Operation completed: {:?}", value));
    }

    fn on_error(&self, error: &Error, context: &ConcernContext) {
        self.logger.error(&format!("Operation failed: {}", error));
    }
}

デザインパターンのフレームワーク化

// パターンレジストリの実装
pub struct PatternRegistry {
    patterns: HashMap<PatternId, Box<dyn Pattern>>,
}

impl PatternRegistry {
    pub fn register<P: Pattern + 'static>(&mut self, pattern: P) {
        self.patterns.insert(pattern.id(), Box::new(pattern));
    }

    pub fn apply_pattern<T>(&self, pattern_id: PatternId, target: T) -> Result<T>
    where
        T: ApplyPattern,
    {
        let pattern = self.patterns.get(&pattern_id)
            .ok_or(Error::PatternNotFound)?;
        pattern.apply(target)
    }
}

// パターン適用のトレイト
pub trait Pattern: Send + Sync {
    fn id(&self) -> PatternId;
    fn apply<T: ApplyPattern>(&self, target: T) -> Result<T>;
}

// デコレータパターンの実装例
pub struct DecoratorPattern<T> {
    id: PatternId,
    decorator: Box<dyn Fn(T) -> Result<T>>,
}

impl<T: 'static> Pattern for DecoratorPattern<T> {
    fn id(&self) -> PatternId {
        self.id
    }

    fn apply<U: ApplyPattern>(&self, target: U) -> Result<U> {
        if let Some(value) = target.as_any().downcast_ref::<T>() {
            let decorated = (self.decorator)(value.clone())?;
            Ok(decorated.into())
        } else {
            Err(Error::TypeMismatch)
        }
    }
}

パターンの自動適用メカニズム

// パターン自動適用のマクロ
#[macro_export]
macro_rules! apply_patterns {
    ($target:expr, $($pattern:expr),*) => {{
        let mut result = $target;
        $(
            result = $pattern.apply(result)?;
        )*
        Ok(result)
    }}
}

// パターン適用の属性マクロ
#[proc_macro_attribute]
pub fn pattern(attr: TokenStream, item: TokenStream) -> TokenStream {
    let pattern_id: PatternId = syn::parse(attr).unwrap();
    let input = syn::parse::<ItemFn>(item).unwrap();

    // パターン適用のコード生成
    quote! {
        #[allow(non_snake_case)]
        pub fn #input.ident #input.sig {
            let result = #input.block;
            PATTERN_REGISTRY.apply_pattern(#pattern_id, result)
        }
    }.into()
}

実装例:ロギングとトレーシングの統合

// ログとトレースを統合した関心事の実装
pub struct LogTrace {
    logger: Logger,
    tracer: Tracer,
}

impl CrossCuttingConcern for LogTrace {
    fn before<T>(&self, value: &T, context: &ConcernContext) {
        let span = self.tracer.create_span("operation");
        self.logger.info_with_span(&span, &format!("Starting: {:?}", value));
        context.set_span(span);
    }

    fn after<T>(&self, value: &T, context: &ConcernContext) {
        if let Some(span) = context.get_span() {
            self.logger.info_with_span(span, &format!("Completed: {:?}", value));
            span.end();
        }
    }
}

// 使用例
#[pattern(log_trace)]
fn process_data(data: &str) -> Result<String> {
    // データ処理ロジック
    Ok(data.to_uppercase())
}

// 実行時の自動適用
fn main() -> Result<()> {
    let result = Concern::new("hello world")
        .map(|s| process_data(s)?)?
        .and_then(|s| validate_data(s))?
        .value;
    
    println!("Result: {}", result);
    Ok(())
}

モナド変換子の実装

// モナド変換子の基本実装
pub struct ConcernT<M> {
    inner: M,
    context: ConcernContext,
}

impl<M, T> ConcernT<M>
where
    M: Monad<T>,
{
    pub fn new(monad: M) -> Self {
        Self {
            inner: monad,
            context: ConcernContext::default(),
        }
    }

    pub fn lift<U>(monad: M) -> ConcernT<M>
    where
        M: Monad<U>,
    {
        ConcernT::new(monad)
    }

    pub fn run(self) -> M {
        self.inner
    }
}

今回のまとめ

  1. モナドベースの関心事分離により、横断的な機能を透過的に適用できる
  2. デザインパターンのフレームワーク化で、パターンの再利用性が向上
  3. 属性マクロによる自動適用で、ボイラープレートを削減
  4. モナド変換子により、異なるモナドの組み合わせが可能

次回予告

第3回では、cusinartフレームワークの分散処理基盤について解説します。透過的な分散処理の実装とノード間通信の最適化を中心に、具体的な実装例を示していきます。

参考資料

  • Aspect-Oriented Programming in Rust
  • Monad Transformers Step by Step
  • Design Patterns: Elements of Reusable Object-Oriented Software
  • Attribute Macros in Rust
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?