書いて覚えるSwift入門

第44回Swiftのモジュールとは

import What?

今回はモジュール(module)について話します図1⁠。モジュールとはいったいなんでしょう?

図1 モジュールとは?
図1 モジュールとは?

macOSにもiOSにも標準搭載されているOxford American Dictionaryにはこうあります。

> _Computing_ any of a number of distinct but interrelated units from which a program may be built up or into which a complex activity may be analyzed.

同じく標準搭載のスーパー大辞林ではこうです。

> ④ ソフトウェアやハードウェアを構成する部分のうち、独立性が高く、追加や交換が容易にできるように設計された部品。

このとおり専門用語を超え一般用語として受け入れられているモジュールですが、コンピュータ言語における扱いは、てんでんばらばらちんぷんかんぷんまとまんない。そもそもCのように言語自体にはモジュールというコンセプトそのものが存在せず、モジュール相当の機能はリンカー(linker)で実現しているものもあれば、それすら永らく存在しなかったJavaScript[1]もあれば、Perl 5 やPythonやRubyのようにはじめから基本機能として組み込まれているものまでさまざまです。Swiftは どうでしょうか?

結論に飛び付く前に実際に試してみましょう。Swift Playgroundでページを新規作成すると、次のようなテキストがはじめから入っています。

//: [Previous](@previous)

import Foundation

var str = "Hello, playground"

//: [Next](@next)

こういう、はじめから入っているものをboilerplateと呼びますが図2⁠、それはさておき、なぜimport Foundationという文言が入っているのでしょう?

図2 boilerplate
図2 boilerplate

では、var str = "Hello, playground"print(sqrt(2.0))と書き換えてみましょう。1.4142135623730951と表示されたはずです。ここでimport Foundationをコメントアウトしてみましょう図3⁠。今度はどうなりましたか?

図3 import Foundationをコメントアウト
図3 import Foundationをコメントアウト

今度は「sqrtなんて知らないよ」と言われます。さらに、sqrt(2.0)をただの2.0に変えてみましょう。どうなりましたか?

このことから次のことがわかります。

  • import Foundationなしでもprint()できる[2]
  • import Foundationしないとsqrt()できない

つまり、sqrt()という関数(function)はSwift本体ではなく、Foundationというモジュールに存在し、importすることで使えるようになるというわけです。

これは、Cの#includeとは明らかに異なります。先ほどのSwift Playgroundの例をCで書くと、

#include <stdio.h>
#include <math.h>

int main(){
    printf("%.17g\n", sqrt(2.0));
    return 0;
}

となりますが、ここで#includeが入った行をすべて削除しても……。

図4のように警告は出てもコンパイルは通ってしまいます。Cの#includeは単に指定されたテキストファイルを挿入しているだけですが、Swiftのimportはもっと本質的に「そのモジュールを利用するにあたって必要なことをすべて行う」という意味になっています。

図4 コンパイルを実行
% cc -Wall sqrt2.c
sqrt2.c:7:5: warning: implicitly declaring library function 'printf' with type
      'int (const char *, ...)' [-Wimplicit-function-declaration]
    printf("%.17g\n", sqrt(2.0));
    ^
sqrt2.c:7:5: note: include the header <stdio.h> or explicitly provide a
      declaration for 'printf'
sqrt2.c:7:23: warning: implicitly declaring library function 'sqrt' with type
      'double (double)' [-Wimplicit-function-declaration]
    printf("%.17g\n", sqrt(2.0));
                      ^
sqrt2.c:7:23: note: include the header <math.h> or explicitly provide a
      declaration for 'sqrt'
2 warnings generated.

それでは「必要なこと」とはいったい何でしょう? こちらも書いて覚えましょう。

making your own module

ここでは実例としてswift-complexを利用してみます。Terminalからgit cloneしてcdしてswift buildしてみましょう図5⁠。

図5 git→git clone→cd→swift buildの流れ
% git clone https://github.com/dankogai/swift-complex.git
Cloning into 'swift-complex'...
remote: Enumerating objects: 1070, done.
remote: Total 1070 (delta 0), reused 0 (delta 0), pack-reused 1070
Receiving objects: 100% (1070/1070), 1.49 MiB ¦ 1.60 MiB/s, done.
Resolving deltas: 100% (519/519), done.
dankogai@dan-imac151[1032]:̃/work% cd swi
swift-complex/ swift/
% cd swift-complex
% swift build
Fetching https://github.com/dankogai/swift-floatingpointmath.git
Completed resolution in 1.02s
Cloning https://github.com/dankogai/swift-floatingpointmath.git
Resolving https://github.com/dankogai/swift-floatingpointmath.git at 0.1.0
Compile Swift Module 'FloatingPointMath' (1 sources)
Linking ./.build/x86_64-apple-macosx10.10/debug/libFloatingPointMath.dylib
Compile Swift Module 'Complex' (2 sources)
Linking ./.build/x86_64-apple-macosx10.10/debug/libComplex.dylib
Compile Swift Module 'ComplexRun' (1 sources)
Linking ./.build/x86_64-apple-macosx10.10/debug/ComplexRun

ここまではうまくいきました。ところがREPLを開いてimport Complexしてみようとすると……図6⁠。

図6 REPLで実行
% swift
Welcome to Apple Swift version 4.2.1 (swiftlang-1000.11.42 clang-1000.11.45.1). Type :help for assistance.
  1> import Complex
error: repl.swift:1:8: error: no such module 'Complex'
import Complex
       ^
  1>

そんなモジュールはないと怒られてしまいます。何が足りないのでしょう? あるはずのComplexモジュールはどこにあるのでしょう?

どうやら.build/debugというディレクトリにあるようですが図7⁠、Swiftはそこにあることを知らないようです。そこにあるということをSwiftに教えてあげるにはどうしたらよいでしょう? 図8が答えになります。

図7 Complexモジュールはどこにある?
% ls .build/debug/
Complex.build                  ComplexRun.swiftmodule
Complex.product                FloatingPointMath.build
Complex.swiftdoc               FloatingPointMath.product
Complex.swiftmodule            FloatingPointMath.swiftdoc
ComplexPackageTests.product    FloatingPointMath.swiftmodule
ComplexRun                     ModuleCache
ComplexRun.build               libComplex.dylib
ComplexRun.dSYM                libComplex.dylib.dSYM
ComplexRun.product             libFloatingPointMath.dylib
ComplexRun.swiftdoc            libFloatingPointMath.dylib.dSYM
図8 Swiftコマンドに設定を追加
% swift -I.build/debug -L.build/debug -lComplex
Welcome to Apple Swift version 4.2.1 (swiftlang-1000.11.42
clang-1000.11.45.1). Type :help for assistance.
  1> import Complex
  2> Complex.sqrt(-1.0)
$R0: Complex.Complex<Double> = {
  real = 0
  imag = 1
}
  3>

Xcodeのプロジェクトであればどこに何があるのかは自動で管理されていますが、裏ではこういうことが行われているわけです。しかしXcodeはmacOSにしかありませんし、これは何とも面倒です。面倒なので、筆者がGitHubで公開しているモジュールはscripts/runrepl.shというシェルスクリプトを同封していますが、これくらい標準でできるようになってほしい……と思ったら、swift run --replでできるようになるようです。

ただし実装されているのは、執筆現在はSnapshot版だけで、リリース版最新のSwift 4.2.1では次のとおり断られます。

% swift run --repl
error: unknown option --repl; use --help to list
available options

importできるならexportはできないの?

ところで、importの有効範囲は、それが宣言された場所からソースファイルの終わりまでです。たとえばモジュールAでimport Bしていたとして、import Aしたソース中ではimport Bしたことにはなりません。import Aしたら同時にimport Bするにはどうしたらよいでしょう? 任意精度(有理数|浮動小数点数)をSwiftで実装したswift-bignumというモジュールもメンテしているのですが、そこでは内部的に任意精度整数モジュールBigIntを使っています。任意精度有理数や浮動小数点数が使える状態で整数が使えないという状況は変なのでimport BigNumしたらimport BigIntも同時に行いたかったのですが、これは次のとおり解決しました。

@_exported import BigInt

アンダースコアが付いていることからも察せられるように、これは現時点ではundocumented featureなのですが、正式にサポートされることを願っています。

iPad Proを(まだ)買わなかった理由

10月30日のSpecial EventでAppleは溜まった宿題をまとめて片付けた感があります。iMac Proに匹敵する性能までカバーしたMac miniにUSB-C化されたMacBook Air、そして脱LightningしたiPad Pro写真1⁠。目玉はなんといってもiPad Proのはずですが、その1週間後に筆者が衝動買いしてしまったのは……MacBook Airでした写真2⁠。

写真1 iPad Pro
写真1 iPad Pro
写真2 MacBook Air
写真2 MacBook Air

理由はただ1つ。iOSにはXcodeがまだないから。Swift自体はSwift Playgroundで使えます。が、いまだにmacOSやLinuxで使えるモジュールが使えない。前述のswift-complexではiOS用のPlaygroundを生成するのに必要なソースをamalgamateするという苦肉の策をとっています。RAM容量を除けばiPad Proのほうがずっと高性能なのに……。

Xcode for iPadを待ちかねつつ、次回はモジュールを軸としたSwiftの開発の実際をお届けする予定です。

Software Design

本誌最新号をチェック!
Software Design 2022年9月号

2022年8月18日発売
B5判/192ページ
定価1,342円
(本体1,220円+税10%)

  • 第1特集
    MySQL アプリ開発者の必修5科目
    不意なトラブルに困らないためのRDB基礎知識
  • 第2特集
    「知りたい」⁠使いたい」⁠発信したい」をかなえる
    OSSソースコードリーディングのススメ
  • 特別企画
    企業のシステムを支えるOSとエコシステムの全貌
    [特別企画]Red Hat Enterprise Linux 9最新ガイド
  • 短期連載
    今さら聞けないSSH
    [前編]リモートログインとコマンドの実行
  • 短期連載
    MySQLで学ぶ文字コード
    [最終回]文字コードのハマりどころTips集
  • 短期連載
    新生「Ansible」徹底解説
    [4]Playbookの実行環境(基礎編)

おすすめ記事

記事・ニュース一覧