import What?
今回はモジュール(module)について話します(図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
では、var str = "Hello, playground"
をprint(sqrt(2.0))
と書き換えてみましょう。1.4142135623730951
と表示されたはずです。ここでimport Foundation
をコメントアウトしてみましょう(図3 ) 。今度はどうなりましたか?
図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
写真2 MacBook Air
理由はただ1つ。iOSにはXcodeがまだないから。Swift自体はSwift Playgroundで使えます。が、いまだにmacOSやLinuxで使えるモジュールが使えない。前述のswift-complexではiOS用のPlaygroundを生成するのに必要なソースをamalgamateするという苦肉の策をとっています。RAM容量を除けばiPad Proのほうがずっと高性能なのに……。
Xcode for iPadを待ちかねつつ、次回はモジュールを軸としたSwiftの開発の実際をお届けする予定です。
第1特集
MySQL アプリ開発者の必修5科目
不意なトラブルに困らないためのRDB基礎知識
第2特集
「知りたい」「 使いたい」「 発信したい」をかなえる
OSSソースコードリーディングのススメ
特別企画
企業のシステムを支えるOSとエコシステムの全貌
[特別企画]Red Hat Enterprise Linux 9最新ガイド
短期連載
今さら聞けないSSH
[前編]リモートログインとコマンドの実行
短期連載
MySQLで学ぶ文字コード
[最終回]文字コードのハマりどころTips集
短期連載
新生「Ansible」徹底解説
[4]Playbookの実行環境(基礎編)