「偏見と妄想で語るスクリプト言語としての Swift」登壇補足

# 2022 年 5 月 31 日追記

本記事内のパフォーマンスについての記述は不備があります、具体的には[本記事の最初のコメント](https://qiita.com/lovee/items/3c405e73992caef782dd#comment-667dcf683217c1804ad8)を読んでいただければと思います；より厳密な検証、追筆等はまた今後時間ある際に補足させてください。

# 前書き

先日、Qiita 様のご厚意で、[Qiita Night というエンジニア向けイベント](https://increments.connpass.com/event/246181/)に参加させていただきました。イベントのテーマが「Swift 6 がもたらす開発者体験を予測しよう！」でしたので、「偏見と妄想で語るスクリプト言語としての Swift」というタイトルで登壇し、またその後トークセッションでもいろいろお話ししました。その時のスライドはこちらからご覧いただけます：

https://speakerdeck.com/lovee/swift-as-a-scripting-language

ここでせっかくなので、時間の都合でいろいろ話せなかった部分を補足としてこの記事を綴らせてください。

# タイトルについて

まず、タイトルの前半がなぜ「偏見と妄想」かというと、実はすでに察した方もいるかと思いますが、本イベントのテーマが「Swift 6」についてですが、アップルが公式で「Swift 6」の明確なリリース時期などをまだ公言していません。現在 GitHub にて公開されてる時期リリースブランチがバージョン 5.7 にとどまっているのです。

ところがご存じの通り、来月に開催が控えられている WWDC の招待状には、Swift のロゴがど真ん中に大々的に載せられており、間違いなく Swift 言語自身に関する重大発表があるはずだと予想できます。その重大発表の内容は開催当日まで明かされませんが、確かにもしかすると次のメジャーバージョンである Swift 6 のスニークピーク的なものの可能性もなきにしもあらずです。ただどのみち現時点では確たる情報が何一つなく、唯一の関連情報は 2 年前に公開した [On the road to Swift 6](https://forums.swift.org/t/on-the-road-to-swift-6/32862) という Swift 6 への展望の記事で、その中に記載されてる目標もいくつかがすでに現在の Swift 5.6 で実現されています。

となると、やはり Swift 6 については妄想するしかないのでは？と思った次第です。

インセプション、という映画をご存じでしょうか。他人の夢に潜入して機密情報を盗んだり、逆にその人にアイデアを植え付けたり、という映画です。中にこのような名台詞があります：You mustn't be afraid to dream a little bigger, darling.（あなたよ、もっととんでもない夢を見てもいいんだよ。）そう、どうせ妄想するんだったら、もっととんでもない妄想をしようと、このセリフを思い出しながら決断したんです。これまでほとんどの人が考えもしなかったことを語ろうっと。

そしてそれに適してるちょうどいい題材があるのです。最近自分は会社の複数のプロジェクトに共通化できる環境構築周りを整備しており、そのハードルを下げるために Ruby などをなるべく排除し、Swift で環境構築を賄おうとしているのです。つまり Swift をまるでスクリプト言語のように使っているのです。Swift の使い道として、iOS や macOS など、いわゆるアップルのエコシステム上のアプリ開発はもちろん誰でも知ってるし、むしろそれ以外知らない人も多いかと思います。なんか頭が沸いてる（褒め言葉）輩なら、Server-Side Swift についてご存じのマニアも多少はいます。でも Swift を敢えてスクリプト言語として使う人はやはりそうそういないじゃないかなと思います。でも私は Swift 大好き人間なので、やはりなんでも Swift で書きたいです。だから「偏見」です。

# スクリプト言語として使うには

では、具体的に Swift をどのようにスクリプト言語として使えばいいかというと、実はすでにいくつかやり方があります。発表の中でも触れたのは [Danger-Swift](https://danger.systems/swift/) というものですが、他にも [fastlane](https://fastlane.tools) のオルタネイティブとして [Swiftlane](https://github.com/onmyway133/Swiftlane) も開発されており、こちらも Swift でスクリプトを書けます。ところが実はこのように外部ライブラリーを使わなくても、一番単純な方法があります。それはズバリファイルの先頭にシバンとして `#!/usr/bin/swift` を書いておけばいいです。そしたらファイルに実行権限を付与すると、他のスクリプトと同じように `./xxx.swift` で実行できちゃうんです：

```swift:test.swift
#!/usr/bin/swift

print("Hello, World!")
```

```terminal
% chmod +x test.swift #←実行権限を付与
% ./test.swift
Hello, World!
```

もちろん、実行権限がなかったり、シバンがなくても、普通に `swift xxx.swift` でも実行できます：

```terminal
% swift test.swift
Hello, World!
```

ただし残念ながら Swift はファイル単位での import ができないので、ロジックが長かったりでファイル分割したい場合は、Swift Package として作る必要がありますが、一応その際もパッケージのディレクトリーに入れば `swift run` で擬似的にスクリプト言語のように動かせます。

# 実際に Swift をスクリプトとして使ったときのパフォーマンスはどうよ

実は単体ファイルだけで、そのファイルを実行するだけでしたら、意外と非常に早いです。1 から 1000 まで数える FizzBuzz プログラムを Shell、Ruby および Swift で同じロジックで書いてみましたが、Swift は案外 Ruby と互角だったりします。

まずは Shell をみてみましょう。下記のロジックですと、筆者の環境では実行するのに 4 秒以上かかることがわかります

```sh:fizzbuzz.sh
#!/bin/sh

fizz_buzz() {
  should_print_num=true
  if [ `expr $1 % 3` -eq 0 ]; then
    should_print_num=false
    printf "Fizz"
  fi
  if [ `expr $1 % 5` -eq 0 ]; then
    should_print_num=false
    printf "Buzz"
  fi
  if $should_print_num; then
    echo $1
  else
    echo ""
  fi

}

A=1

while [ $A -le 1000 ]; do
  fizz_buzz $A
  A=`expr $A + 1`
done
```

```terminal
% chmod +x fizzbuzz.sh
% time ./fizzbuzz.sh > /dev/null
./fizzbuzz.sh > /dev/null  0.79s user 2.59s system 79% cpu 4.249 total
```

![shell.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63998/227a8aff-b733-3415-3b5f-e3df1c06a29c.gif)

そして Ruby ならだいぶ早くなり、筆者の環境ではわずか 0.14 秒で終わります

```ruby:fizzbuzz.rb
#!/usr/bin/ruby

(1..1000).map do |i|
  shouldPrintNum = true
  if i % 3 == 0
    shouldPrintNum = false
    print "Fizz"
  end
  if i % 5 == 0
    shouldPrintNum = false
    print "Buzz"
  end
  if shouldPrintNum
    puts i
  else
    puts ""
  end
end
```

```terminal
% chmod +x fizzbuzz.rb
% time ./fizzbuzz.rb >/dev/null
# ./fizzbuzz.rb > /dev/null  0.05s user 0.05s system 68% cpu 0.145 total
```

![ruby.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63998/62b36aa9-9001-24e0-4b94-3d5996b8f03e.gif)

では Swift はどうかというと、今回筆者の場合は Ruby よりもさらに早い 0.121 秒で終わったのです：

```swift:fizzbuzz.swift
#!/usr/bin/swift

for i in 1 ... 1000 {
  var shouldPrintNum = true
  if i % 3 == 0 {
      shouldPrintNum = false
      print("Fizz", terminator: "")
  }
  if i % 5 == 0 {
      shouldPrintNum = false
      print("Buzz", terminator: "")
  }
  if shouldPrintNum {
      print(i)
  } else {
      print("")
  }
}
```

```terminal
% chmod +x fizzbuzz.swift
% time ./fizzbuzz.swift >/dev/null
# ./fizzbuzz.swift > /dev/null  0.07s user 0.04s system 84% cpu 0.121 total
```

![swift.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63998/41df412e-3798-c955-3f56-47a6d66d43ee.gif)

もちろんここまで早く終わってしまうと、速度差が実質誤差みたいなものですので、とにかく shell より遥かに速いことさえわかれば十分ではないかとは思います。

ただ前にも書いた通り、これはあくまでソースファイル単体を動かした場合の話であって、ファイル分割すると Swift Package で作る必要が出てくるので、そうするとビルドにかなり時間がかかってしまいます。とは言え、それも初回だけの話で、キャッシュさえあればソースファイル単体で動かすときとあまり変わらないです。これはスライドにも指摘しております。

# 後書き

何はともあれ、来月の WWDC を期待しながらも、Swift の使い道がさらに広がれればいいなと、切に思います。