モジュールの分割法
前回はswift-floatingpointmathを紹介したところで終わりました。これはもともとモジュールではなくswift-complexの一部として実装されていましたが、
swift-floatingpointmathは何をするモジュールかといえば、
- 0.FloatingPointMathというプロトコルの定義
- 1.protocol extensionによる(Cにおける <math.やJSにおけるh> Mathオブジェクトが提供する)数学関数の提供 
- 2.DoubleおよびFloatのFloatingPointMathへの準拠
実際に中身を見てみましょう。まずはプロトコルの定義から。
public protocol FloatingPointMath {
    init (_:Double)
    var asDouble:Double { get }
}Doubleから初期化可能であること、Doubleへ変換可能であること。つまりDoubleとの相互変換が可能である型asDoubleではなく、
public Double {
    init(_:FloatingPointMath)
}に相当する定義もprotocol FloatingPointMath{}の内部ではありますが、asDoubleというプロパティを用意しています。
protocol extension
次にprotocol extensionを見てみましょう。次のような定義が、
extension FloatingPointMath {
    public static func acos (_ x:Self)->Self {
        return Self(
            Foundation.acos (x.asDouble)
        )
    }
    // ...
}要するにDoubleに変換してからFoundationの数学関数で計算した結果をもとの型に戻しているだけですが、FloatingPointMathに準拠している型はこれらの数学関数を自前で実装しなくても、Doubleで計算したのと同様の結果を得ることができるわけです。
そして、DoubleおよびFloatもFloatingPointMathに準拠させるために、
extension Double : FloatingPointMath {
    public var asDouble:Double { return self }
    public static func acos (_ x:Double)->Double {
        return Foundation.acos(x)
    }
    /// ...
}
extension Float : FloatingPointMath {
    public var asDouble:Double { return
Double(self) }
}という最低限の拡張を施しています。Doubleで数学関数を別途定義しているのは、
一見このモジュールは単なる無駄に見えます。import Foundationすれば同モジュールが提供している機能はすべて使えるのですから。
しかし、Foundationには2つの問題があります。
- 0.あまりに多くの関数がトップレベルにimportされる
- 1.組込みの数値型しか使えない
たとえばlogといってもMath.とConsole.ではまるで別の意味になりますが、Foundationのlogは問答無用でMath.の意味になります。その一方でNSLogは後者の意味なのですからややこしい。数学関数だけ、importする手段がSwift本体には用意されていないのです。
しかしそれだけであればFloatingPointMathをわざわざ定義せずとも、Foundation.と明記すればいいだけです。実際にこの記法はFloatingPointMathのprotocol extensionでも用いられており、return Foundation.をreturn acos(x)と書いたら無限再帰してしまいます。
しかし組込みの数値型しか使えないのは問題です。せっかくSE-0067で浮動小数点が固定的な型ではなくプロトコルとして整理されたのに、DoubleとFloatしか使えないのでは宝の持ち腐れというものではないですか。
実際筆者はswift-bignumというパッケージで任意精度有理数BigRatと任意精度浮動小数点数BigFloatを提供しており、FloatingPointプロトコルに準拠しています。しかしFloatingPointMathの数学関数はFloatingPointプロトコルでは未定義なので、FloatingPointMathプロトコルをパッケージとして独立させたうえで利用するようにしま
した。
エレガントに複素数を扱う
こうして
public protocol ComplexNumeric : Hashable {
    associatedtype Element: SignedNumeric
    var real:Element { get set }
    var imag:Element { get set }
    init(real:Element, imag:Element)
}すなわち符号付数値SignedNumeric)Element)
public typealias ComplexFloatElement =
FloatingPoint & FloatingPointMath
public protocol ComplexFloat : ComplexNumeric &
CustomStringConvertible
    where Element: ComplexFloatElement {
}と、ElementがFloatingPointかつFloatingPointMathに準拠しているプロトコルとして定義されたうえで、structとしてComplex型が定義されています。
public struct Complex<R:ComplexFloatElement> :
ComplexFloat {
    public typealias NumericType = R
    public var (real, imag):(R, R)
    public init(real r:R, imag i:R) {
        (real, imag) = (r, i)
    }
}つまりComplex型の要素はDoubleやFloatだけではなく前述のBigRatやBigFloatもそのまま使えるということです。
PONSの爆誕
数学的な数値の性質をプロトコルとしてまとめ、
図1と図2はPONSの型とプロトコルの相関関係
 
 
次回予告
今年はSwiftがリリースされてから5年目。不要だった機能は削ぎ落とされ、
本誌最新号をチェック!
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の実行環境(基礎編)

