型とは何か?
今回は型
型がなければ意味がない
およそどんなプログラミング言語にも登場するこの型という概念ですが、
筆者はこう答えます。
Software Designの読者であれば100%ご存じのとおり、
01100011011011110110010001100101
これが何を意味するか、
フラグが32個並んでるだけかもしれませんし、1668244581
かもしれませんし、0x636f6465
を指すポインタかもしれませんし、6.
のことかもしれませんし、code
のことかもしれませんし、rgba(99,111, 100, 0.
のことかもしれません……。
つまり、
型はイイが型付けはイタい
コンピュータに意味ある仕事をさせるのがプログラマの仕事なのですから、value
)type
)
主な言語をざっと見てみると、
public class Hello {
public static void main(String[] args){
System.out.println("Hello, world!");
}
}
型を指定しているpublic class
も、public static void
も、main
の括弧のなかにあるString[]
も省けません。そしてソースコードの名前はHello.
でなければなりません。そして動かすにはTerminal.
から、
% javac Hello.java % java Hello
とします。
ずいぶん面倒ですが、javac
したら次回以降はjava Hello
だけで実行できます。Hello.
をゴミ箱にポイしてもjava Hello
で実行できます。ls
してみるとHello.
というファイルができています。これをJavaがインストールされた別のコンピュータにそのままアップロードしたうえでjava Hello
と命じるとやはりそのまま動きます。まさにWrite once, run everywhere
なわけです。
そして書かなくてもいいプログラミング言語として、
console.log("Hello, world!");
とたった1行。動かす方法はいくつもあるのですが、
% node hello.js
でjava
のjavac
相当は不要です。これくらい短いとソースコードすらファイルに落とさずとも、
% node -e 'console.log("Hello, world!");'
で動いてしまいます。紙幅制限ゆえ、
- ソースコードとは別の実行用ファイルを出力するタイプの言語では型を明記する。CやC++がこれに相当
- ソースコードを直接実行するタイプの言語では型を省略できる。いわゆるスクリプト言語、
Perl、 PHP、 Python、 Rubyがこれに相当
という大まかな傾向には賢明な読者のみなさんはすでにお気づきと思います。
ここで本記事の主題であるSwiftがやっと登場します。Swiftはどっち?
ソースコードはこうです。
print("Hello, world!")
この1行をhello.
という名前のテキストファイルにセーブしたら、
% swift hello.swift
で実行できてしまいます。ところが、
% swiftc hello.swift
とするとhello
というファイルが生成されて、
./hello
でやはり実行できてしまいます。ただしJavaとは異なり、
つまりSwiftはスクリプト言語のように型を略記でき、executable
)compile
)
名を持つ値の型
“Hello, world!”Hello, world!
という文字列の値をそのまま使っていましたが、
let hello = "Hello "
let world = "world!"
print(hello + world)
一度データを指定したらそのまま最後まで使うものを定数constant
)variable
)identifier
)
比較のためにRubyを見てみましょう。次のv
は値だけではなく型
irb(main):001:0> v = 42 => 42 irb(main):002:0> v.class => Fixnum irb(main):003:0> v = 42.195 => 42.195 irb(main):004:0> v.class => Float
しかし、
% swift
Welcome to Apple Swift version 4.1
(swiftlang-902.0.48 clang-902.0.39.1). Type
:help for assistance.
1> var v = 42
v: Int = 42
2> v = 42.195
error: repl.swift:2:5: error: cannot assign
value of type 'Double' to type 'Int'
v = 42.195
↑
Int( )
Swiftでは、var
でなくてlet
にしたら」

型は値から推論される
本記事の主題は型なのに、type inference
)
値、
let hello = "Hello "
のHello
というリテラルliteral
)String
であることは一目瞭然です。42が整数Int
、Double
であることも。
それでは関数function
)+
する関数はこうなります。
func plus(_ l:Int, _ r:Int)->Int {
return l + r
}
これでplus(40, 2)
は42
になります。簡単ですね。しかし今度はplus(42.
としてみてください。cannot convert value of type 'Double' to expected argument type 'Int'
というエラーになるはずです。
今度はさきほどのfunc plus
を残したまま次を追加しています。
func plus(_ l:Double, _ r:Double)->Double {
return l + r
}
今度はplus(40, 2)
もplus(42.
も期待どおりの答えを返したはずです。関数の名前が同じでも引数argument
)return value
)
しかしこのやり方だと、
あります。総称型generics
)
今度は先ほどのfunc plus
を両方消して、
func plus<T:Numeric>(_ l:T, _ r:T)->T {
return l + r
}
どっちもうまく行ったことが確認できるでしょう。先ほどと違うのは<T:Numeric>
という表記。これはNumeric
というプロトコルprotocol
)comply
)T
という意味になります。そしてInt
もDouble
もNumeric
というプロトコルに準拠しているので、
型の関数と(Double,Double)->Double
型の関数を兼ねることができるわけです。
ところでSwiftは値から型を推論すると先ほど述べました。そして関数自体も値です。ということは名前のない関数リテラルに推論可能な引数を与えたら、
それに気づいた読者は鋭い。
({$0 + $1})(40, 2) // 42:Int ({$0 + $1})(42.0, 0.195) // 42.195:Double
({$0 + $1})
だけではダメで、({$0 + $1})
が(Int,Int)->Int
で(Double,Double)->Double
だという推論が成立するので、
これが配列Array
)Dictionary
)Collection
)
(1...100).reduce(0){ $0 + $1 } // 5050
と型を明示せずに書けますし、
もその正体は総称関数なので、
(1...100).reduce(0,+)
とさらに簡単に書けます。実際に二項演算子+
が関数であることは
(+)(40, 2)
(+)(42.0, 0.195)
のように確認できます。
まとめ
もう10年以上前にPerl 6をHaskellで実装して全スクリプト言語プログラマの度肝を抜き、
Type 😊
Typing 🙁
邦訳すると
本誌最新号をチェック!
Software Design 2022年9月号
2022年8月18日発売
B5判/
定価1,342円
- 第1特集
MySQL アプリ開発者の必修5科目
不意なトラブルに困らないためのRDB基礎知識 - 第2特集
「知りたい」 「使いたい」 「発信したい」 をかなえる
OSSソースコードリーディングのススメ - 特別企画
企業のシステムを支えるOSとエコシステムの全貌
[特別企画] Red Hat Enterprise Linux 9最新ガイド - 短期連載
今さら聞けないSSH
[前編] リモートログインとコマンドの実行 - 短期連載
MySQLで学ぶ文字コード
[最終回] 文字コードのハマりどころTips集 - 短期連載
新生「Ansible」 徹底解説
[4] Playbookの実行環境 (基礎編)