1
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?

【大公開?第1弾】Cargryの難解型推論ライブラリ「Actoa」

1
Last updated at Posted at 2026-08-03

F#のコンピューテーション式の知識を前提としています

知らない方でもなるべくわかるように書こうと思います

関数型システムプログラミング言語Cargryを開発しています

今日は、Cargryの型推論ライブラリActoaについて解説しようと思います

Actoaって?

「Actoa」という名前はライブラリ名で、型推論の名前はMonadic Lambda Walk Type-Inference (MLW) です

私の知る限りでは、他の型推論とは異なるものです(そもそも仕組みから「推論」と言えるのやら...)

サンプルコード

見ただけでは何が何だかわからないと思いますが、とりあえず見てください

解説

MLWに登場する型は以下の4つです

  • PseudoPointer:推論の結果を記録・保持するものです
  • MLWGrammarLeaf:そこまで意味はありません。以下2つをまとめるものです
  • MLWTypeVar:型変数です
  • MLWFunction:型変数に対して作用させるものです。引数はタプルで、自由に設定できます

まず、MLWTypeVar作成時に、PseudoPointerへ書き込みがなされます。サンプルコードでは以下になります

変数名
gl1 unknown some
gl2 unknown none
gl3 unknown none

ここで、unknownは未確定の型、somenoneは型に付随する補足情報です(Rustで言うと、どこの型変数の借用なのか、何のトレイトを実装しているかなど)

そして、38行目から40行目にかけて型推論を行います

38行目

38行目は

let res = gl1.function.execute_function(gl1.type_var);

です

gl1のクロージャの定義から、PseudoPointer

変数名
gl1 "Obj" some
gl2 unknown none
gl3 unknown some

に変わります。この時、gl1resに変わります

39行目

let (res2, _) = gl2.function.execute_function((gl2.type_var, res));

です

gl2のクロージャの定義から、PseudoPointer

変数名
gl1 "Obj" some
gl2 ref gl1 unknown none
gl3 unknown some

に変わります。gl2res2に変わります

unifyを呼ぶと、selfと、selfを指す変数は、引数を指すようになります。元の値はそのまま放置されます

40行目

let (_, _) = gl3.function.execute_function((gl3.type_var, res2));

です

gl3のクロージャの定義から、PseudoPointer

変数名
gl1 "Obj" some none
gl2 ref gl1 unknown none
gl3 ref gl1 unknown some

に変わります


gl2gl3gl1を指していることがわかります

これによって、gl1gl2gl3は全て同じ型になります

なぜコンピューテーション式の知識が必要?

MLWのアイデアがこれから始まっているためです

サンプルコードは

R = lambda(
        T,
        [T = "Obj"]
        => lambda(
            U,
            [U <- T]
            => lambda(
                S,
                [
                    S <- U,
                    add(U, sub some)
                ]
                => S
            ) (unknown sub some)
        ) (unknown sub none)
    ) unknown sub some)

を表したものです

MLWの特徴

MLWの特徴としては「同じ型でも『違う型』とする」というところです

「この2つが同じ型」か、「あれはそれに関連する型」と言わなければいけません


また、「実行フローも表せる」という特徴もあります

サンプルコードで言えば

fn f(v1: Obj) -> Obj {
    let v2 = v1;
    v2
}

を表しています

gl1gl3をみると、「v1が使われ、返り値になる」となります

fn f(v1: Obj) -> Obj {
    let v2 = v1;
    Obj::new()
}

となると、gl3gl1を指さなくなります

終わりに

MLWはHindley-Milner型推論の実装が面倒くさくて生まれたものです

Cargryの型推論はこれでやっていきます

1
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
1
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?