断絶と再学習
Go言語は以下を理由として好き(。・ω・。)ノ♡となり2015年頃からハマっていた。
- コンパイル型言語の中では理解しやすい。
- インストールファイルが比較的小さい。
- ワンバイナリ出力が便利
- コンパイルも高速だと感じる。
- 標準ライブラリだけでWebサーバ構築できる
- 3d Party ライブラリが豊富で、インストールもコマンドだけで完了できる。等
しかし、2018年から2022年のモジュール化推進によるプログラム作成方法の改変が進んだ。改変の期間中で、Web記事を読んだモジュール化推進の理由は「依存しているライブラリのversion管理地獄👹問題」の解消の為と知った。
背景は理解できるし、困っている人が存在する事も想像できるが、私がGo言語を好き❤️といっても、毎日何か作る🍳わけでもなく、たまに趣味でツールを作成する程度であり、私自身はライブラリのversion管理で悩む🤔事は無かった。
しかし、この改変は、私の10年前の知識が役に立たなくなる改悪👿となった。
2015年頃にGo言語の解説本を2冊買った。どちらも私にとってバイブル📔で現在でも使えるが、一部の記述は使用不可となった。
- 「基礎からわかるGo言語 改訂2版」
- 「スターディングGo言語」
Webサイトも同様で、5年以上の前の記述では、コンパイルが通らない場合がある。解説がわかり易くても、使えない記述となった。
Go言語は、後方互換性を大切にすると聞いており、例えば「1.18のジェネリクス導入」「1.23のイテレータの実装」は新文法の新規導入・仕様追加なので、後方互換性が維持されるのは理解できるが、モジュールや環境設定の面では後方互換性が断絶されたと感じる。😢
今から、Go言語を始める人にとっては、昔の知識は不要で、最新の仕様を学べば良いが、私にとってモジュール化推進は目👀を背けたくなる歴史であった。
昨今の私のプログラムブームはターミナルで動く簡単なゲーム🎮制作だが、C++やRustも、多少やってみたが、なかなか難しく、かゆい所に手✋が届かなった。
結局はGo言語に戻ってきて、再学習✍️が必要となった。
Go言語Version年表
Go 1.0 :2012/03/28
Go 1.1 :2013/05/13 Go Modules が導入され、依存管理が進化
Go 1.2 :2013/12/01
Go 1.3 :2014/06/18
Go 1.4 :2014/12/10 解説本「基礎からわかるGo言語」の前提
Go 1.5 :2015/08/19
Go 1.6 :2016/02/17 解説本「スターディングGo言語」を対象
Go 1.7 :2016/08/15
Go 1.8 :2017/02/16
Go 1.9 :2017/08/24
Go 1.10:2018/02/16
Go 1.11:2018/08/24 Go1.11からのGo Modulesでは相対パスImportは使用不可となる。
Go 1.12:2019/02/25
Go 1.13:2019/09/03 Go modules がデフォルトで有効となる。
Go 1.14:2020/02/25
Go 1.15:2020/08/11 セルフホスティング(Go自身でGoをコンパイル)に移行
Go 1.16:2021/02/16 Go-Moduleによるプロジェクト構成が標準で推奨される
Go 1.17:2021/08/16
Go 1.18:2022/03/15
- go get によるインストール機能は削除された。
go get は使わず go install を使わなければならない。 - ジェネリクス(Generics) が導入され、言語仕様に変化
Go 1.19:2022/08/02
Go 1.20:2023/02/01
Go 1.21:2023/08/08
Go 1.22:2024/02/06
- for ループの2つの仕様変更
- 変数がイテレート毎に宣言されるようになる。
Go 1.23:2024/08/06
Go 1.24:2025/02/04
Go 1.25:2025/08/05
モジュール化の基本の考え方
以下は、私がGo言語の理解を進める為の個人的定義です。
厳密な定義は違うのでご注意下さい。
- Go言語には「モジュール」「パッケージ」「ファイル」が存在し、それぞれ名前には、関連付け(依存)の為の名付けルールがある。
- 「モジュール名」「パッケージ名」「ファイル名」はURLと同じように扱っているのでASCII文字がほぼ強制で、日本語は困難。
- 「モジュール」「パッケージ」は事実上フォルダ(=ディレクトリ)📁と考える。
- 「モジュール」は全体フォルダ📁や上位フォルダ📁と考える。
- ルートディレクトリ=最上位フォルダ名=プロジェクト名は、必ずしも「モジュール名」と一致する必要はない。規模が小さければ一致しても良い。
- 「パッケージ」は個別フォルダ📁と考える。
- 「モジュール」は、同じフォルダ内にあるソースファイルの集合。
- 「パッケージ」も、同じフォルダ内にあるソースファイルの集合。
- modファイルに、「モジュール名」と「バージョン」を記載する。依存する他のモジュール名も追記可能。
- 外部ファイルで使用される関数名の頭文字は大文字。
- importは、相対パスは使用不可となり、モジュール名開始パス(絶対パス)となった。
フォルダ構成
📁pj_folder/
├📄go.mod
│ └module module_name_ABC
│
├📄app.go
│ ├package main
│ ├import (module_name_ABC/pkg_folder_2_layer)
│ ├func foo(){略}
│ ├func main(){
│ │ new_pkg_folder.Func_of_2_layer()
│ │ foo()
│ │}
│ └func init(){略}
│
└📁new_pkg_folder/
└📄app2.go
├package new_pkg_folder
└func Func_print_of_new_pkg(){略} //外部出力なので、頭文字が大文字
サンプルファイル
module module_name_ABC
go 1.21.3
package main
//import "モジュール名/読み込むパッケージ名=フォルダ名"
import (
"fmt"
"module_name_ABC/new_pkg_folder"
)
func foo() {
fmt.Println("3.foo")
}
func main() {
// 別パッケージの関数は頭につけるパッケージ名を付ける。関数の頭文字は大文字
new_pkg_folder.Func_print_of_new_pkg()
foo()
}
func init() {
fmt.Println("1.initは最初に実行される")
}
package new_pkg_folder // パッケージ名はディレクトリ名と同じにする
import (
"fmt"
)
func Func_print_of_new_pkg() {
fmt.Println("2.新パケージの関数を実行")
}
実行結果
go run .\app.go
1.initは最初に実行される
2.新パケージの関数を実行
3.foo
補足
package mainを分割して複数のGoファイルを、go runで認識させる方法
go run .
または
go run *.go
感想
私のプログラム知識は、html,css,JavaScript,VBA,C#(Visual studio),C言語(超初級)が前提となり、その上での感想です。
今回のGo言語の理解の第一の障壁は、関数の頭文字を大文字にする命名規則だ。この制約は「慣習・慣例・推奨」でなく「強制」だ。
html,css,JavaScriptが知識ベースの私にとっては苦痛となった。
この規則は、関数名が英語しか受け付けられない狭量(心が狭い)と傲慢を感じるが、Go言語の設計者のケン・トンプソンが戦前生まれのおじいちゃん👴で、プログラムは英語しか知らない世界で生きてきたからなのだろうか。
現状のVisual Studio codeの文字検索では、大文字🔠も小文字🔡も区別なく検索結果に挙がるので、プログラム誤記だと認識するのに時間がかかってしまう。(気づかない)
go.modの中に、モジュール名の宣言を記載するが、モジュール名を忘れ、どこに、どの順番でプログラムに配置したら関連付けられるのか忘れる。第二の障壁として、この「ど忘れ」は脳の記憶力(メモリ)の問題がある。「概念」は、考え方=演算式=CPU的なので、記憶力は頼らなくても、少しのヒント💡があれば思い出せるが、配置はノーヒントとなり思い出せない。要は「年取ると覚えてられない!」という事だ。
今回は切羽詰まったので、意思を持って再学習をすることになった。好奇心と探求心🔬と少しの辛抱😣が必要だ。
main関数が入っているGoファイル名を「わかり易いから」と「main.go」にして説明する本やWebサイトやYou Tuberがいるが、学習者にとってmain関数を入れるファイル名は「main.go」にしなければならない?という疑問と混乱を生じさせる事に気づかないだろうか?
そんなこと当たり前すぎて、気に留めないだろうな。AIの時代が来たらやさしく、辛抱強く、順序だてて教えてくれるのだろうか。
参考リンク