はじめまして。2022年4月に中途入社し、Analytics Delivery Divisionでフロントエンド/バックエンドチームに所属しているエンジニアの井上です。前職からWeb開発に従事しており、ARISEに入社してからはWeb/モバイルアプリ開発チームにジョインして、Go言語(以下Go)を使ったAPI開発を担当しています。今回はGoの概要や特徴、またARISEが開発言語にGoを採用した理由を知っていただきたいと思い、記事を作成しました。
Go言語の概要
GoはGoogleが作成したプログラミング言語で、静的言語なコンパイラ言語です。Robert Griesemer, Ken Thompson , Rob Pikeらによって2009年に公開されました。Goを使ったプロダクトにはDockerやTerraformなどがあります。
Goが登場するまでに、ソフトウェアはJavaやC++などの1990年代に登場した言語によって開発されていました。1990年代からコンピューターは進化してきた一方で、プログラミングする行為自体はあまり進化してこなかったことにGoの開発者たちは課題を感じていました。例えばサーバーのソフトウェアを開発する言語を使用するために複雑な準備をする必要があることに不満を感じていたようです。そのため開発者の生産性を高くして、スケーラブルにしたいというコンセプトでGoを作成しました。
Goのメリットは大きく2つあります。1つ目は構文が最小限のためシンプルな設計であることです。Goは他の言語によくあるwhileやforeachなどの構文が実装されていません。このシンプルさが開発者にとってラーニングコストが低い言語になっています。2つ目はGoは同じ書き方がされやすいことです。Goはgofmtという自動でソースコードを整形してくれる公式の機能があります。そのため開発者間で記述の差が少なく、読みやすく、メンテナンスしやすくなります。
一方でGoのデメリットはシンプルな設計のためコードが長くなりやすいこと、クラスがないためクラスベースのオブジェクト指向の記述ができないといったことがあります。
他言語との比較
Web開発で使われるPHPとJavaと比較していきます。
PHPは動的型のオブジェクト指向のインタプリタ言語です。動的に生成されるWebページを開発者が速やかに作成するというコンセプトで作成されています。世界の40%以上のサイトがWordpressというPHPを用いて開発されたサービスを使ってといわれるほど普及しています。
PHPは動的型言語のため変数や引数に型の指定がなく、比較的簡単にプログラムが作成できます。一方で型のミスマッチにより実行時にエラーが発生する可能性もあります。また注意しないとXSSなどが起きる可能性があります。
Javaは静的型のオブジェクト指向のコンパイラ言語です。JVMという仮想マシンがあるところではどこでも実行できるという特徴があります。金融システムやEvernoteがJavaを用いて作られています。
Javaはクラスベースのオブジェクト指向を確立した言語で、広く普及していて使用者も多いことから大規模開発に向きます。またセキュリティが言語レベルで高いことがメリットですが、実行時に仮想マシンを動かす必要があるためメモリ消費が大きいというデメリットがあります。
実装してみた。
それではバブルソートを例に各言語を比較してみます。1から1000までの数をシャッフルして、バブルソートで並び替えをするプログラムをGo, PHP, Javaで実装してみました。
まずはソースコードから。
■Go
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
var array []int
for i := 1; i <= 1000; i++ {
array = append(array, i)
}
array = shuffle(array)
start := time.Now()
bubbleSort(array)
fmt.Println(time.Since(start).Microseconds())
}
func shuffle(array []int) []int {
rand.Seed(time.Now().UnixNano())
rand.Shuffle(len(array), func(i, j int) {
array[i], array[j] = array[j], array[i]
})
return array
}
func bubbleSort(array []int) {
for i := 0; i < len(array); i++ {
for j := 1; j < len(array); j++ {
if array[j-1] > array[j] {
tmp := array[j-1]
array[j-1] = array[j]
array[j] = tmp
}
}
}
}
■PHP
<?php
function main () {
$array = range(1,1000);
shuffle($array);
$start = microtime(true);
bubbleSort($array);
$end = microtime(true);
echo ($end - $start) * 1000000;
}
function bubbleSort($array) {
for($i = 0; $i < count($array); $i++) {
for ($j = 1; $j < count($array); $j++) {
if ($array[$j - 1] > $array[$j]) {
$tmp = $array[$j - 1];
$array[$j - 1] = $array[$j];
$array[$j] = $tmp;
}
}
}
}
main();
?>
■Java
import java.util.*;
public class Main {
final static int length = 1000;
static int[] array = java.util.stream.IntStream. rangeClosed(1, length).toArray();
public static void main(String[] args) throws Exception {
int[] arrayShuffled = shuffle(array);
long startTime = System.nanoTime();
bubbleSort(arrayShuffled);
long endTime = System.nanoTime();
System.out.println((endTime - startTime) / 1000);
}
static int[] shuffle(int[] array) {
for (int i = array.length - 1; i > 0; i--) {
int r = (int) (Math.random() * (i + 1));
int tmp = array[i];
array[i] = array[r];
array[r] = tmp;
}
return array;
}
static void bubbleSort(int[] array) {
for (int i = 0; i < array.length; i++) {
for (int j = 1; j < array.length; j++) {
if (array[j - 1] > array[j]) {
int tmp = array[j - 1];
array[j - 1] = array[j];
array[j] = tmp;
}
}
}
}
}
書き心地
動的型のPHPは型を指定することなく変数定義とメソッドを作成できるため、比較的簡単にコードを書けると感じました。一方で静的型のJavaは変数定義もメソッドを作成も型を記述する必要があり、冗長に感じました。
さて、静的型付け言語のGoの変数定義はメソッド内では “変数名 := 値“ と書くことで型推論をしてくれます。例えば数値型の0を値として持つ変数i, jを以下の2つの書き方で定義できます。
var i int = 0
j := 0
書き心地が動的言語のようにできます。ちょっとしたことですが記述が簡単だと感じました。
処理時間比較
それでは気になる処理時間を見ていきましょう。単位はマイクロ秒です。
インタプリタ言語のPHPはGoと比較して桁が一つ多くなっていました。そしてGoは同じコンパイラ言語のJavaよりも4倍程度速いですね。速度面でもGoは素晴らしい言語ですね。
Go言語をARISEのプロジェクトで採用した理由
我々のプロジェクトは長期間の開発が見込まれて、ある程度の開発者入れ替わりが考えられ、また参画するメンバー間の技術スタックが揃いにくい状況にあります。
その条件下でGoはシンプルで理解しやすいこと、開発者間での表記ブレが起こりにくいこと、さらにシンプルで可読性が高いことが決め手となり採用されました。
実際にプロジェクトに採用してみての良し悪し
実際にGoで開発していて感じた良かった点は「エラー制御の可読性が高いこと」だと思います。
他の言語ではtry – catch – finallyの形式のエラーハンドリングを使いますが、コードが複雑化して可読性が損なわれる可能性もあります。一方でGoはエラーを変数に持たせることができるためメソッドからエラーを返して制御することができます。
例えば割り算をして結果を表示するプログラムを考えてみます。
package main
import (
"errors"
"fmt"
)
func divide(a, b int) error {
if b == 0 {
return errors.New("0で割り算できません")
}
fmt.Println(a / b)
return nil
}
func main() {
err := divide(4, 2)
if err != nil {
fmt.Println(err)
}
err = divide(2, 0)
if err != nil {
fmt.Println(err)
}
}
0で割り算をすることはできないので、devideメソッドの第二引数に0が入った場合はエラーを返すようにしました。このコードを実行すると17行目で呼ばれるdevideは2を出力し、21行目呼ばれるdevideはエラーを返します。このようにエラー処置がとても分かりやすくて簡単に書けることが良い点ですね。
逆に他の言語に比べて便利なライブラリが少ないことがデメリットだと感じました。
JavaやPHPにはオープンソースの便利なライブラリが多数ありますが、それに比べるとGoは歴史が浅いせいかそのようなライブラリが少ないように思います。これから成熟していく発展途上な言語なのかもしれません。
まとめ
今回はGoの概要をJava, PHPと比較して紹介しました。この記事を読んで少しでもGoに興味を持っていただけると幸いです。次はGoの特徴の一つである並行処理についての紹介記事を書いていきます。お楽しみに!