notebook

知ったこと、気になったこと、気づいたことを書き残す。

golangの循環importにハマった

tl;dr

やる気失せた。

循環import

golangには循環importというエラーがあります。

error| go list:Error:import cycle not allowed

簡単に言ってしまえば、パッケージ同士が互いをimportし合っていたらコンパイルエラーとなります。
パッケージAがパッケージBをimportしていて、パッケージBでもパッケージAをimportしていたら循環importの出来上がりです。

パッケージA → パッケージB → パッケージA → パッケージB → ...

設計をしっかり考えていないと割とハマります。
私はライブ感でコーディングしてたらハマってしまいました。プログラムの構成から考え直さなきゃいけないので中々修正する気が出ません。(:3」∠)

対策

対策と言っても、コンパイルレベルのエラーである以上、循環importにならない設計を心がけるとしか言えなそうですが、一応、

  • インターフェースを利用する

    golangではinterfaceを実装する場合にはimplements宣言とかいらず、interfaceが定義されているパッケージをimportする必要もありません。
    interfaceに沿った関数を提供すれば実装したことになりinterface型で扱えるようになります。(ダックタイピングってやつなんでしょうか?)
    これを利用すれば、使われる側から使う側をimportせずに参照できるようになります。
    詳しく説明するのは面ど...大変なので調べてみてください。DIとかそういう話にもなってきたりならなかったり。

  • 全部同じパッケージにする

    バカアホ

まとめ

何も考えず思いつきで実装していたツケが回ってきました。これからはコンパイラに怒られないように気をつけようと思います。
golangのinterface周りは活用できればいい感じのコードを書けそうですね。テストも書きやすくなりそう。
この記事書きながらコードいじってたら直せそうな気がしてきたのでもう少し頑張ります。
くりーんあーきてくちゃ?なにそれ

Goの' := 'の妙

tr;dr

  • := では再代入は原則できない

  • := の右辺が返す型が多値の場合は再代入できる(例外あり)

:= について

暗黙的な定義ってやつ。
型を書くのが面倒なことが多いのでよく使ってしまいますよね。
この記事を書くにあたって調べてみたけど、あくまで変数の定義の糖衣構文であって、代入の式ではないらしい。

これはOK

var a int = 1 // 変数の定義 + 代入
a = 2 // 再代入

var b := int型を返す関数()  // 変数の定義 + 代入
b = 2 // 再代入

これはNG

var a int = 1 // 変数の定義 + 代入
a := int型を返す関数() // コンパイルエラー

b := int型を返す関数()  // 変数の定義 + 代入
b := 2 // コンパイルエラー

:= を使った再代入はできないことが分かります。

関数が多値を返す場合は定義済みの変数に再代入できる

多値とは以下のように、複数の型の値を返せるやつです。

func multi() (int, error) {
    return 1, nil
}

a, err := multi()

タプルっぽいけど型では無いのでタプルでは無いっぽい。
エラー処理とかでよく使いますよね。

多値を返す関数が右辺の場合

以下はコンパイルエラーにはなりません。

func multi() (int, error) {
    return 1, nil
}

var a int = 0
a, err := multi() // 再代入できる!

変数a は代入済みなのに:=を使って再代入ができることが分かります。

例外

以下のようなパターンはコンパイルエラーとなります。

左辺を同じ変数のペアにして再度:=を使う場合

func multi() (int, error) {
    return 1, nil
}

a, err := multi()
a, err := multi() // コンパイルエラー (no new variables on left side of :=)

新しい変数を宣言する必要があるようです。
以下ならコンパイルエラーは起きません。

func multi() (int, error) {
    return 1, nil
}

a, err := multi()
b, err := multi()

左辺の変数のどちらかを新しい変数にすると再代入できます。(もちろん変数errの方を新しい変数にしても問題ありません)

左辺で_を使って変数に代入する場合

goですごいと思ったのは_ が特殊な意味を持っているという点です。
多値を返す関数を右辺にした場合、左辺のいずれかの変数を_という名前で定義しておくと、その変数_ は使わなくてもコンパイルエラーとならなくなります。(goでは定義済みの変数が未使用だとコンパイルエラーとなります。)

この_:= を同時に使った場合はコンパイルエラーとなります。

func multi() (int, error) {
    return 1, nil
}

var a int = 0
a, _ := multi() // コンパイルエラー (no new variables on left side of :=)

新しい変数の定義が必要

上記ふたつの例外で起こるコンパイルエラーですが、ともにno new variables on left side of := というエラーメッセージが出ます。
読んで字の如く、「:=の左辺には新しい変数が必要」っていうことが分かります。

つまり、:=を使って定義済みの変数に再代入する際には、新しい変数とペアにした上で右辺の多値を返す関数の返り値を代入する必要がある。ということが分かります。

まとめ

:= を使って再代入っぽいことはできます。
ただ、変数への再代入自体が昨今は微妙と考えられている(と思っている)のであまり多用せず、関数ごとに新しい変数を宣言するのが良いのでは無いかと思います。
go難しいよ。go

二週間くらいvim使って思ったこと

tl;dr

  • 操作は普通に難しい。
  • 慣れないと開発効率は落ちる。
  • プラグインが便利。

vim使い始めた

vimを本格的に使い始めた。
もともと、VSCodeとかeclipseとかでvimの操作ができるようにするプラグインを使っていて移動系の操作には慣れていたけど、二週間前くらいからvim本体を使い始めた。
自宅と会社のPCにそれぞれvim入れて使っている。
ちなみに会社はwindowsgvimメインで使っている。意外とgvim便利ですよね。自宅はubuntuなのでvim

難しいところ

  • 思考にタイピングが付いていかない

    まだ不慣れなだけかもしれないが、やりたいアクションに対してのキーボードでの入力に時間がかかってしまう。
    コマンドとかモーションとかを入力する際に(理解が足りないため)、「頭の中で考える→タイピングする」という一通りの流れにめちゃめちゃ時間かかってしまう。
    また、ミスタイプも多いため、間違えてコマンド打ってしまって変な状態になってしまうとかがよくある。(これは最近キーボードを新調した影響も大きい)

  • 覚えることが多い

    カーソル移動からして独特だし、コマンドも多機能のため、エディタ上の操作だけでも覚えることが多い。
    また、vimrcとかをいじり始めると設定値とか文法とかも理解していく必要があるため、ここでも調べたりするのに時間がかかってしまう。(幸いにもググれば大体の答えは出てくる印象。それだけコミュニティが昔から活発なんだと思う。)

総じて、VSCodeeclipseよりも直感的に操作できるものでは無いという印象を抱いている。
また、ユーザーによって好みのカスタマイズをするのが前提のエディタだと捉えているので、会社とかで設定を義務付けたりすべきものでは無い。そういった意味ではやっぱり趣味の領域になってしまいがちなのかな、とも思う。

良いところ

  • カスタマイズ性

    ユーザーのカスタマイズ前提で作られているエディタだと思うので、UIレベルから自分好みにできる。
    ちゃんと使い始める前は、真っ黒なターミナルで動かすイメージしかなかったけど(サーバーとかに入っているviとかのイメージ)、外観も割と自由に変えられたり、(プラグインを使って)ディレクトリツリーを表示させてIDEっぽくできたりと予想以上にカスタマイズ性が高くてびっくりした。

f:id:uraven0107:20210910234726p:plain
こんな感じの画面なら簡単に作れる

まとめ

vim楽しいよvim
難しいし、まだまだ理解不足&不慣れだけど、カスタマイズ性の高さはかなり気に入ってたのでこれからも使い続けていこうと思う。
あと、CLIがもともと好きなのでシェルとかCLIツールとかとも併せてどんどん使っていきたいです。

"n"でnodejsのバージョン管理する。

what's "n" ?

nというツールでnodejsのバージョン管理ができる。

インストール

$ sudo npm install -g n
$ n --version

ltsのnodejsをインストール

lts版なのでこれやっとけばとりあえず良さそう。

$ sudo n lts
$ node -v

最新版のnodejsをインストール

$ sudo n latest
$ node -v

特定のバージョンのnodejsをインストール

$ sudo n install <version>
$ node -v

インストール済みのnodejsのバージョンに切り替える

$ sudo n
> インストール済みバージョン一覧が表示されるので希望のバージョンを選択
$ node -v

インストール済みのnodejsのバージョン一覧を確認する

$ n list

ちなみに

aptとかで先にnodejsをインストールしている場合でも問題なく使える。
nを使ったnodejsのインストール先が、(ubuntuだと) /usr/local/bin/node にインストールされるため、PATHでそのディレクトリが先に参照されるようになっていれば、こっちが優先される。

$ where node
/usr/local/bin/node    ←nでインストールされるディレクトリ
/usr/bin/node    ←aptでインストールされるディレクトリ
/bin/node    ←aptでインストールされるディレクトリ

$ which nodejs
/usr/local/bin/node

おしり。

【2021】今年やりたいこと

2021年も8月半ばになってしまったが、今年中にやりたいことをまとめる。
お酒飲みながら書いているので見直したら変わりそう。

スキルアップ

  • Go言語

最近触っている。
触る前に思ってた感じと少し違ったけど、完全に趣味でやってるので気楽に進める。
実際にゼロからプログラム作って学習しているのでいい感じにインプット出来ている。

業務でまた使うことになりそうなので。
なんだかんだ好きな言語なので、仕様的な部分もしっかり把握しておきたい。
Spring関連も

  • React

仕事で生のJavascriptjQuery使ってDOM操作してるのに嫌気が差してきたので、SPAフレームワークのアーキ的な知識を身に着けておきたい。
以前、自己紹介ページを作ったことあるけど完全に忘れたので学習し直したい。

Reactの勉強も兼ねてブログでも作りたい。

  • Docker

ほぼ知識0で業務でも使う目処が無さそうなので最低限の使い方は身につけておきたい。
mysql込みの開発環境をサクッとコンテナ管理できるようになりたいな。。。

せっかく実機で持っているのでもっと使いこなしたい。
コマンドは基本的なのは使えるので、もっと低レイヤの知識深をめる。(コマンドもオプションとかをもっと把握しておく必要あり)

  • shell

zsh使っているけどshellの基本的な部分すら理解できていない。
本読むだけじゃ身につかなそうなのでスクリプトガリガリ書いていきたい。

せっかくアカウント作ったのでチャレンジしていきたい。 転職活動で思ったけど、アルゴリズムを考えられる能力はエンジニアには必須。

QOL

  • キャッシュレス

スマホ決済を活用したい。d払いがいい感じ?
クレカもviewcardに一本化してしまって良い気がしている。(あるいは楽天カードとか)

  • サービス

便利そうなサービスをガンガン使いたい。
見つけるのが大変。アンテナを貼る。

お仕事 & お金周り

  • 転職

念願のSpringで開発をしている会社に入社することになったよ!!!!!! 技術的には新しいって感じの場所では無さそうだけど、これまで以上にエンジニアとしてのスキルアップがしていけそうだとは感じているので頑張りたい。

  • 投資

証券口座あるんだし、資産運用考えたほうが良さそう。

私生活

  • ダイエット

完全にリバウンドしたので戻したい。
ジムは近いほうが、いい!

  • 食生活

自炊をもっと習慣づけようと思う。
今でもそこそこしているけど気を抜くと外食しちゃう。外食は水曜日のみにする。

積みプラをいい加減消化しよう。

  • 家電

ドラム式洗濯機がほしい。

  • 引っ越し

転職で通勤が長くなるので、検討中。
どのみち来年の2月で契約更新のタイミングなので良い機会だと思う。

今年(からしばらく)のモットー

とりあえず、やってみる。

QueryExceptionのgetCode()はintとは限らないらしい

どういうこと?

以下のようなコードを動かしたらifブロック内に入ってくれなかった。

try {
    // QueryExceptionが出るコード
} catch (QueryException $qe) {
    if ($qe->getCode() === 23000) {  // intの比較
        // ここに入ってほしい
    }
}

公式を覗く

Laravelの公式APIドキュメントには特に情報なし。QueryExceptionクラスはPDOExceptionクラスを継承しているのでそっちのドキュメントを見に行った。

PHP: PDOException - Manual

/ Inherited methods /

...

final public Exception::getCode ( ) : int

intじゃないか/(^o^)\

さらに親のExceptionクラスにgetCode()は定義されてるっぽい。

getCode()は色んな型を返すかもよ!

Exceptionクラスの更にgetCodeメソッドのドキュメントがあった。

PHP: Exception::getCode - Manual

Returns the exception code as int in Exception but possibly as other type in Exception descendants (for example as string in PDOException).

↓翻訳

Exception では例外コードを int として返しますが、Exception の子孫では他のタイプとして (たとえば、PDOException の文字列として) 返す可能性があります。

ちょっと翻訳が変だけどPDOExceptionではstringで返すかもよ!って書いてある。

まとめ

こうしたら無事にifブロック内に入りました。

try {
    // QueryExceptionが出るコード
} catch (QueryException $qe) {
    if ($qe->getCode() === '23000') { // 文字列の比較 
        // やったね!
    }
}

PDOExceptionってPDO(DBの種類意識しないで済むやつ)の機能の一部だからDBによってgetCode()の型が変わったりするのかな。

上記のコードはMySQLの環境で実行してたけど他のDBで試す気力はなかった。。。

==の比較にすればこの辺意識する必要なさそう?動的型付言語の謎は深まるばかり。