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

Typstにおける「関数呼出可能な値」

Posted at

関数呼出」というのはfoo("bar")のような式のことで、Typstユーザなら誰でも使ったことがあるでしょう。この記事で話題にしたいのは「そもそもfoo(引数)という関数呼出が文法的に可能なのはfooどの型の値である場合か」ということです。

多くの人は「え、関数呼出可能なのは関数でしょ:neutral_face:」と考えたでしょう。確かに、関数(function値)は関数呼出可能です。でも実は、Typstにはfunction以外にも「関数呼出可能」な値をもつ型が存在します。

型値(type値)

型値」というのはトップレベルでstrint等で参照できる1「型を表す値」であり、Typstのプログラミングにおいて入力値の型を調べたいときに使われます。

(コードモード)
if type(input) == str {
  // inputは文字列(str型)
} else if type(input) == int {
  // inputは整数(int型)
} else {
  // それ以外
}

でもよく考えてみると、“strintという名前のもの”は「(文字列や整数に)型変換する」場合にも用いられます。

(マークアップモード)
// int値をstr値に変換する(16進法)
#str(129414, base: 16)
// float値をint値に変換する
#int(calc.pi)

image-2.png

この式はどう見ても「関数呼出」です。とすると、strintは関数(function値)なのでしょうか? type()で調べてみましょう。

(マークアップモード)
#type(str) #type(int)

image-3.png

やっぱり「型値」はtype2という独自の型を持っているのでした。つまり、「関数だけでなく型値も関数呼出が可能である」ということになります。

型値で関数呼出をする用法のことを「コンストラクタ(constructor)」と呼びます。Typstの型についてのドキュメント(例えばstr型)を見ると“Constructor”という節が置かれている場合がありますが、これはstr(引数)のように関数呼出をする用法についての説明です。

image-1.png

コンストラクタの処理内容は型によってまちまちです。「当該型への型変換」である場合が多いですが、必ずしもそうとは限りません3。ただ恐らく「当該型の値を返す」処理である、というのは成り立ちそうです。

注意点として、全ての型値がコンストラクタの用法をもつわけではありません。例えば、bool(真偽値)やlength(長さ値)は関数呼出ができません。

(コードモード)
bool(0)
//==> ❌エラー: type boolean does not have a constructor
length(0)
//==> ❌エラー: type length does not have a constructor

Typstのこのような型値の性質はPythonによく似ている感じがしています。

シンボル(symbol値)

Typstの数式モードでarrowと書く4と“→”の記号が出力されます。

(マークアップモード)
$A arrow B$

image-4.png

Typstの文法マニアの人ならこのarrowsymbol型の値だということは知っているでしょう(多分 :upside_down:

ところが、accent()関数のドキュメントにあるように、このaccentは関数呼出の形式で「文字に“→”のアクセントを付ける」のにも使用できます。

(マークアップモード)
$arrow(x)$

image-5.png

これだけ見ると「数式関数呼出だから何か変なことが起こっている」という気もしますが、そうではありません。このarrowは数式外ではsym.arrowとして参照できますが、数式外のsym.arrowもやはり関数呼出できます。つまり、「シンボル(symbol値)も関数呼出が可能である」というわけです。

(マークアップモード)
#sym.arrow /
// これは"sym.arrow([0])"と同等なので関数呼出である
#sym.arrow[0]

image-6.png

例によって、全てのシンボルが関数呼出可能であるわけではありません。恐らくaccent()関数がサポートしている記号に限られるのでしょう。

(マークアップモード)
// "emoji.face.inv"もsymbol値だが…
#emoji.face.inv[z]
// ==> ❌エラー: symbol 🙃 is not callable

symbol型の値はsymbolのコンストラクタでユーザが生成できます。この場合も対象の文字が「サポートしている記号」に含まれるならば生成した値は関数呼出可能になるようです。

(マークアップモード)
// "arrow.l.r"と同じ矢印(U+2194)
// ※ここでは"([0])"の丸括弧を省略できないことに注意
#symbol("↔")([0])

image-7.png

関数モドキと関数の差異

ここまで「関数モドキ」、すなわち「関数ではないが関数呼出が可能な値」の話をしてきました。関数モドキはほとんどの場合に関数と同じように振る舞いますが、挙動が異なる部分もあります。
function型のメソッドにwith()というものがあり、これは「引数を部分適用する」という機能をもちます。

(マークアップ―モード)
// 赤色で出力する関数
#let redden = text.with(fill: red)

// "text(fill:red)[great!]"と同等になる
Typst is #redden[great!]

image-8.png

これを見ると、str()でも同じことができそうな気がします。しかしstrのような関数モドキはfunction値ではないため、当然functionのメソッドは使えないのです。

(マークアップ―モード)
// "16進法の文字列に変換する関数"を作りたいが…
#let hex-str = str.with(base: 16)
// ❌エラー: type string does not contain field `with`

こういう場合は「関数モドキを本物の関数に変換する処理」を挟む必要があります。

(マークアップ―モード)
// 16進法の文字列に変換する関数
// ※関数リテラルで書いたものは当然関数(function値)である
#let hex-str = ((..args) => str(..args)).with(base: 16)

// "str(base:16, 129414)"と同等
#hex-str(129414)

image-9.png

まとめ

というわけで皆さん、「Typst Advent Calendar 2025」は明日以降の記事(または昨日の記事:blush:)に期待しましょう!:information_desk_person:

  1. あるいはstdモジュールを利用して、std.strstd.intでも参照できます。

  2. ちなみに、type(str)で返る(“type”と表示される)値はtype(str)typeと同じものです。つまり、type(str)も「型値で関数呼出を行う」例になっています。

  3. 例えば先ほどのtype(str)はコンストラクタですが、これは型変換とは呼び難いですね。

  4. “→”の出力には->という短縮記法を使うことが多そうですが。

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