KotlinのSecondary Constructorsでvalのプロパティを初期化したい

やりたかったこと

firstに依存しているsecondを保持するPairのようなモノが欲しかった。

data class Pair<F, S>(
    val first: F? = null,
    val second: S? = null
)

fun <F, S> newPair(first: F?, block: (F) -> S?) = Pair(
    first = first,
    second = first?.let(block) // firstがnullでなければblockを呼び出す
)

newPairは、以下のような使い方を想定している。

val pair = newPair(getFirst()) {first ->
    getSecond(first)
}

firstsecondともにNullableだがblockの実行はfirstがnullではない状態を保証したい。

別にこのままでも良いんだけど、ちょっとコンストラクタにしてみようと思いたった。このままでも良かったのにKotlin力が高くないので、アレコレ試行錯誤が必要だった。あとcompanion objectという方法もあったけど、まぁやってみたかったんだよ。

thisへの委譲では式が使える

Secondary Constructorsでは、Primary Constructorか他のSecondary Constructorへの委譲が必要になる。thisを使用して呼び出しを行うが、その際には式を利用することができる。なので、こういう呼び出し方ができる。

data class Pair<F, S>(
    val first: F? = null,
    val second: S? = null
) {
    // OK
    constructor(first: F?, block: (F) -> S?): this(first, first?.let(block))
}

このことがわかってなくてあれこれ試行錯誤した。

ダメな例

プロパティがvalであるということが問題になった。

data class Pair<F, S>(
    val first: F? = null,
    val second: S? = null
) {
    // NG: Val cannot be reassigned
    // secondはvalなので書き換えができない
    // コンストラクタなのになんで初期化できんのじゃーとかちょっと思ったりする
    constructor(first: F?, block: (F) -> S?): this(first) {
        second = first?.let(block)
    }

    // NG: Unresolved reference: sec
    // thisの方に変数を渡せるのでは???と思ったけど渡せなかった
    constructor(first: F?, block: (F) -> S?): this(first, sec) {
        val sec = first?.let(block)
    }

    // OK
    // ifも式なのでこれでも問題ない
    constructor(first: F?, block: (F) -> S?): this(first, if (first != null) block(first) else null)

    // OK
    constructor(first: F?, block: (F) -> S?): this(first, first?.let(block))
}

実行例

以下のように期待する動きをしている。

val p1 = Pair("aaa", "AAA")
val p2 = Pair("aaa", String::toUpperCase)
val p3 = Pair<String, String>(null, String::toUpperCase)

println(p1)
println(p2)
println(p3)

// output
// Pair(first=aaa, second=AAA)
// Pair(first=aaa, second=AAA)
// Pair(first=null, second=null)

継承のところで気づいた

リファレンスの継承の辺りを読んでいる時に、以下の記述を見つけて、これはPrimary Constructorの委譲でも使えるのではと思って試したら動いた。

class Derived(
    name: String,
    val lastName: String
) : Base(name.capitalize().also { println("Argument for Base: $it") }) {

...

リファレンスはCollectionの辺りまで読んだつもりだけど、どっか明確に記載されてたかなぁ…

depからgo modへ移行する

昔作ったツールを整理しようと思ったので、ついでにパッケージ管理をgo modにした。

予習

Go Modulesについてふわっとしか知らないので、改めて確認しておく。

以下を参考にした。

とりあえず、

  • 今まで通りGOPATH配下で
  • GO111MODULE=onにする

と良さそうなので、そうした。

作業

ひとまずgo mod initする。

% go mod init
go: creating new go.mod: module github.com/kawaken/funi
go: copying requirements from Gopkg.lock

% cat go.mod
module github.com/kawaken/funi

go 1.12

require gopkg.in/yaml.v2 v2.2.2

go.modが作成された。

不要になるファイルを削除する。

% rm Gopkg.*
rm: remove regular file 'Gopkg.lock'? y
rm: remove regular file 'Gopkg.toml'? y

% rm -fr vendor/

この状態でビルドしてみる。

% go build
go: finding gopkg.in/yaml.v2 v2.2.2
go: finding gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405
go: downloading gopkg.in/yaml.v2 v2.2.2
go: extracting gopkg.in/yaml.v2 v2.2.2

 % cat go.sum
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

問題なさそうなので、コミットする。

% git status -s
 D Gopkg.lock
 D Gopkg.toml
?? go.mod
?? go.sum

% git add go.mod go.sum

 % git rm Gopkg.lock Gopkg.toml
rm 'Gopkg.lock'
rm 'Gopkg.toml'

% git commit -m "dep to go mod"
[master e300c2d] dep to go mod
 4 files changed, 8 insertions(+), 51 deletions(-)
 delete mode 100644 Gopkg.lock
 delete mode 100644 Gopkg.toml
 create mode 100644 go.mod
 create mode 100644 go.sum

もともとvendorディレクトリは管理してないんだった。

gopkg.inはどうする?

公式のWikiに以下のように記述されているので問題なさそう。

  1. gopkg.in

Existing code that uses import paths starting with gopkg.in(such as gopkg.in/yaml.v1 and gopkg.in/yaml.v2) can continue to use those forms for their module paths and import paths even after opting in to modules.

おしまい

2018年振り返りと2019年やりたいこと

今年もやっとくか。

2018年振り返り

個人

大きな出来事といえば、やはり下垂体卒中で入院していたこと。入院の原因となった病状自体は治癒したものの、手術の影響で疲れやすいマンになってしまった。(気になる人はこの辺読んでみて)

今でも月一程度で血液検査して数値を確認する、という経過観察を続けている。徐々に回復傾向にあるが波もあり、突然体調悪くなることもあって油断できない。特に先月はかなりしんどかったし、正直言えば一ヶ月くらいまた休みたい。まぁムリだけど。

ともあれ、これがキッカケで色々と環境を変えていく必要があるな、と踏ん切りがついたし、あまり悲観的にはなってない。

あと一時期、ひたすらに自分用アプリのコードを書いていたが、最近は帰ってくると疲れ気味なので進捗が出せていない。

つくるぞ!って年初に書いたキーボードを作れたので良かった。実際には設計からやろうと思っていたけど、自分の思っているものに近いキーボードが手に入ったので、それで満足してる。

仕事

踏ん切りがついたんで、実際に異動した。とりあえず馴染めていると思う。

やはり少人数の方が意思決定が速くて、仕事が進むのも速いんで飽きがこない。仕事するための調整ごととも(マネージャーの采配なのかもしれないが)離れているので、モヤモヤすることもなく、穏やかな気持ちで仕事に取り組めている。

思い切って環境変えて良かった。とはいえ、ちょっとまだ自分ならではの価値が出せてないと感じている。

2019年やりたいこと

個人

作りかけのアプリを完成させたい。アプリ作成はネタが溜まっているので、形にしていきたい。他にも色々やりたいこともあるし、1日の中での時間の作り方を工夫しなければ。あとは、パソコンとかディスプレイとか買い換えたい。

体調面では、体重がまた10キロ増えてしまったので、痩せたい。

仕事

評価の関係もあり、なんかアウトプットしないといけないんだけど、どんなことがいいか悩ましい。最近は、勉強会とかで発表できるほどのこともできていないし。

今後やっていきたい領域に悩みがあるので、その辺もちょっとゆっくりして考えたい。

意外に書くことないな〜。なんかいつもと比べてイライラしてないからかな?まぁとにかくがんばろ。

GradleでIntelliJ IEDAのプロジェクトを作成する(途中まで)

Kotlin入門2日目:Macでの環境構築の続き。IntelliJはインストール済み。

前回はGradle, Kotlinの導入が終わったところで、Gradleから素のKotlinプロジェクトを作成したところだった。今回はIntelliJのプロジェクトを作成する。

Gradleを使ったプロジェクト作成

まずは前回同様、プロジェクトを作成する。

$ cd sample/
$ gradle init --dsl kotlin --type=kotlin-application \
--package=sample --project-name=sample

BUILD SUCCESSFUL in 0s
2 actionable tasks: 2 executed

The IDEA Plugin

ここを参考に、作成されたbuild.gradle.ktsプラグインを追加する。コメントを除いて以下のようになった。

plugins {
    id("org.jetbrains.kotlin.jvm").version("1.3.10")
    application
    idea    // <- 追加
}

repositories {
    jcenter()
}

dependencies {
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
    testImplementation("org.jetbrains.kotlin:kotlin-test")
    testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
}

application {
    mainClassName = "sample.AppKt"
}

プラグインによってタスクが追加されている。

IDE tasks
---------
cleanIdea - Cleans IDEA project files (IML, IPR)
idea - Generates IDEA project files (IML, IPR, IWS)
openIdea - Opens the IDEA project

ideaタスクを実行する。

$ gradle idea

> Task :idea
Generated IDEA project at file:///private/tmp/sample/sample.ipr

Deprecated Gradle features were used in this build, making it incompatible with Gradle 6.0.
Use '--warning-mode all' to show the individual deprecation warnings.
See https://docs.gradle.org/5.0/userguide/command_line_interface.html#sec:command_line_warnings

BUILD SUCCESSFUL in 15s
4 actionable tasks: 4 executed

IntelliJ からはfile:///private/tmp/sample/sample.iprを開くと良いっぽい。

IntelliJで開く

Gradleの方でopenIdeaというタスクがあるので、実行してみる。

$ gradle openIdea

> Task :idea
Generated IDEA project at file:///private/tmp/sample/sample.ipr

Deprecated Gradle features were used in this build, making it incompatible with Gradle 6.0.
Use '--warning-mode all' to show the individual deprecation warnings.
See https://docs.gradle.org/5.0/userguide/command_line_interface.html#sec:command_line_warnings

BUILD SUCCESSFUL in 1s
5 actionable tasks: 5 executed

この後IDEAが開いたがUnlinked Gradle project?と出てきた。

f:id:kentaro_kawano:20181216235205p:plain

時間なくなったんで今日はここまで。

GradleでKotlinのプロジェクトを作成する

Kotlin入門1日目:まずはMacでの環境構築。Intellijはインストール済み。

GradleとKotlinのインストール

どちらもbrewを使ってインストールする。Javaもいるんでインストールしておく。

brew cask install java
brew install kotlin
brew install gradle

JavaはOpenJDK11が入るみたい。

とりあえずKotlinの動作確認

Working with the Command Line Compiler - Kotlin Programming Language

こちらを参照して、以下のコードを実行してみる。

$ cd /tmp
$ cat <<KT > hello.kt
fun main(args: Array<String>) {
    println("Hello, World!")
}
KT
$ kotlinc hello.kt -include-runtime -d hello.jar
$ java -jar hello.jar
Hello, World!

ひとまず動いた。

Gradleを使ったプロジェクト作成

Creating New Gradle Builds

とりあえずGradleの方で初期化がいるのかな。

$ mkdir hello-kotlin
$ cd hello-kotlin/
$ gradle init --dsl kotlin

Select type of project to generate:
  1: basic
  2: groovy-application
  3: groovy-library
  4: java-application
  5: java-library
  6: kotlin-application
  7: kotlin-library
  8: scala-library
Enter selection (default: basic) [1..8] 6

Project name (default: hello-kotlin):
Source package (default: hello.kotlin):

BUILD SUCCESSFUL in 7s
2 actionable tasks: 2 executed

途中でプロジェクトのタイプを聞かれたので、6: kotlin-applicationにしてみた。

$ tree
.
├── build.gradle.kts
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle.kts
└── src
    ├── main
    │   ├── kotlin
    │   │   └── hello
    │   │       └── kotlin
    │   │           └── App.kt
    │   └── resources
    └── test
        ├── kotlin
        │   └── hello
        │       └── kotlin
        │           └── AppTest.kt
        └── resources

13 directories, 8 files

こんな感じのファイル構成になっている。とりあえず作成されたコードは以下。

$ cat src/main/kotlin/hello/kotlin/App.kt
/*
 * This Kotlin source file was generated by the Gradle 'init' task.
 */
package hello.kotlin

class App {
    val greeting: String
        get() {
            return "Hello world."
        }
}

fun main(args: Array<String>) {
    println(App().greeting)
}

ひとまず実行してみる。

$ gradle run

> Task :run
Hello world.

Deprecated Gradle features were used in this build, making it incompatible with Gradle 6.0.
Use '--warning-mode all' to show the individual deprecation warnings.
See https://docs.gradle.org/5.0/userguide/command_line_interface.html#sec:command_line_warnings

BUILD SUCCESSFUL in 12s
3 actionable tasks: 3 executed

やはり初回の起動は遅い。必要なライブラリとかをダウンロードしたりするからかな。あとなんかWarning出てるし。いろいろと気になることはあるものの、とりあえず今日はここまで。

次回はIntelliJ IDEAのプロジェクト構築をGradleからやってみたい。

自作キーボードのLily58を作った

7日に届いて17日に完成した。平日の夜と週末にやって10時間はかからなかったと思う。このビルドログ書くほうが時間かかってるかも。

買ったもの

今回は持ち運びを前提にしていたので、薄くするためにKailhのロープロキーと組み合わせて作った。キーキャップはキーカスタマイズするので、刻印がないものにした。

金額に特に不満はないけど、だいだい全部で17,000円くらい。

組み立て

公式のビルドガイドはこちら。(久しぶりに見たらアップデートされてかなり丁寧に書いてありました)

Lily58/buildguide_jp.md at master · kata0510/Lily58

特に注意が必要だったところを書いておく。

ダイオードの取り付け

足の折り曲げはビルドガイドにあった通り、アクリル板を3枚重ねて曲げると楽だった。アクリル板はとりあえず洗濯バサミで挟んで固定していた。

折り曲げるとこんな感じになる。

底面側から足を通す。これは右手側になる。

マステで止めてひっくり返してはんだ付けする。マステで止めた段階で、先に足を短くしてしまっても良いかもしれない。足をカットしながらでないと、邪魔になるのではんだ付けしにくかった。

Pro Microの取り付け

Pro Microは付属のコンスルーを差し込んではんだ付けをする。はんだ付けした後にPCBにコンスルーを差し込む。片方しかはんだ付けしない、という点に注意する。

差し込んだ後にPro Microが簡単に抜けてしまったり、PCBを揺らすとカタカタしたりするといった感じで、とにかく緩そうだったら注意が必要。

自分はしっかり取り付けたつもりだったが接触不良になっていた(後述)。

※最近更新されたビルドガイドではどちらもはんだ付けするように記載されているので、そうしたら問題なくなるはず。

両方取り付けたらこんな感じになる。

アクリル板とPCBの重ね合わせ

ダイオードを取り付けた後の、はんだの仕上がり部分(山になったところ)が邪魔してしまい、アクリル板とPCBがピッタリ重ならない。 そのためロープロファイルの軸だと、スイッチの足がPCBに届かない状態になってしまう。

なので、はんだの山をニッパで切り取って薄くする作業を行った。次の写真の真ん中のとこが削ったところ。

なかなか地味に時間がかかるし面倒な作業だったが、これでようやくピッタリ気味で重ねることができた。

※ここもビルドガイドでは表面実装のダイオードが推奨されている

キースイッチの取り付け

この状態でもキースイッチの足はちょっとしか出てこないので、はんだ付けが難しかった。 コテ先をスルーホールに突っ込んで温め、ハンダを流し込む気持ちでやったら上手くいった感じがする。

念のためキースイッチをはんだ付けした後に、テスターで各キーがきちんと導通するか確認を行った。(組み立てが終わってからの動作確認で上手くいかなかったので、この段階で確認しといて良かった)

ファームウェアの書き込み

ソフトウェア的には特に困ることはなかった。

Pro Microのリセット

片方で、リセットのスイッチがうまく動作しなかったため、Pro MicroのRSTGNDを直接ショートさせてリセットした。

Pro Micro & Fio V3 Hookup Guide - learn.sparkfun.com

この辺を参考にした。

動作確認

組み立ても終わりファームウェアの書き込みも終わったが、どうにもうまく動かない。左側はbしか反応しないし、右側はそもそも電源が通ってなさそうだった。

キースイッチの確認

たまにキースイッチが壊れていて反応しないケースがあるらしい。キースイッチをはんだ付けしたタイミングで各スイッチの導通チェックを行っていたので、スイッチの破損や不良がないことは確認済みだった。

ファームウェアの書き込みの確認

PC側での作業の結果(ターミナルでの出力)からファームウェアの書き込みは正しくできていそうだったので、コンスルーの足を直接ショートさせてみた。ピンの組み合わせに応じたキーが入力されたので、ファームウェアの書き込みには失敗していないと確認が取れた。

PCBとPro Microの接続

Discordで教えてもらったので、Pro MicroをPCBに差し込み直したり、コンスルーとPCB接触部分をいじってみたりすると入力できるキーが増えたので、ここに問題があったらしい。コンスルーがやはりどうしても抜けそうな感じがするので、しっかりと折り曲げたりとかしてきちんと接続ができるようにした。結果、問題なくキーが入力できるようになった。また右側についても問題なく動作するようになった。

参照したブログを失念してしまったが、行や列が全然反応しない場合にはこのあたりの接続不良が原因の場合があるらしい。まさに自分もそうだった。

※最新のビルドガイド通りにちゃんとはんだ付けしておけば問題ないはず

使ってみて

完成したのはこんな感じ。キーキャップの種類と数を間違ってしまって、黒が足りなかった。ホントはホームポジションのとこだけ白くするつもりだったけど。

MacBookとの接続の時に、アダプタの周りがごちゃっとしてしまう。MacBook本体からコネクタの部分までのケーブルが長めのアダプタに変えれば問題ないかな。

これはLily58の問題ではないけど、どうもキーキャップが滑る感じがして、ホームポジションのおさまりが悪く誤タイプが最初は多かった。なので、ホットグルーで認識のためのポチを作った。そのおかげか、慣れたからか、ここ数日はホームポジションを間違うこともなくなった。

おわりに

ハンダ付けからファームウェアの書き込みまでは特に問題なく進んだが、PCに接続してからの動作確認が大変だったので、今後作る機会があれば以下のようにしてみようかと思った。

  • ProMicroとコンスルーを接続する
  • この段階でファームウェアの書き込みを行い、各ピンをショートさせキー入力が行えることを確認する
    • ここでうまく行ってなければProMicroの不良やファームウェアの書き込みに失敗していると判断できる
  • PCBにダイオードやキースイッチをはんだ付けしていき、各キースイッチでの導通チェックを行う
    • ここで導通チェックすることで、キースイッチに問題がないことが確実になる
  • PCBとProMicroを接続する
    • この後の動作確認においては、キースイッチとProMicroの動作確認は取れているので、上手くいかない場合にはPCBとProMicroの接触不良が怪しい

とにかく、全部組み合わせてしまってからの動作確認は大変なので、こまめにチェックしていくのが大事だな。

この文章は9割くらいiPhoneで書いていて、仕上げを作成したLily58で書いた。とても使い心地が良くて満足している。

Twitterを制限してNuzzle/Reederを使うようにした

Twitterついついダラダラと見続けてしまうので、良くない。iOSの最近入った機能のスクリーンタイムを使って使用時間を制限することにした。1日30分。

とはいえ情報収集の側面もあるので、代替手段としてNuzzle/Reederを使うようにした。どちらも以前は使っていたものの、ここ数年はほとんど使うことがなかった。

Nuzzle

Twitterのアカウントを連携するとTL状に現れたURLをサマリーしてくれる。アプリでも確認できるし、デイリーのメールも送ってくれる。

人気や話題の記事は漏らさずに拾ってくれてるので「えっそれは知らなかった」みたいなこともない。

あとやたらとURLを含むツイートをたくさんするアカウントがあると、けっこうノイズ感がある。まぁこれはTwitterのTL見ててもそうなんで、仕方ないんだけど。

Nuzzleで拾った記事で良さそうだったらRSS購読するという流れにしている。

Reeder

RSSリーダーアプリでFeedlyのフロントとして使っている。 RSSだけだと新規にフィードが増えていかないし徐々に不活性になってしまうので、それを補うためにNuzzleを使う。

Nuzzleで書いたように、Twitterで見かけたブログ記事の内容が良かったらそのブログを購読するようにしていた。

さっき確認したら登録フィード数は170位あったが、もちろん毎日全部のサイトが更新されるわけではない。1日10件程度で、タイトルだけで判断して興味がなければ見なければ良い。

おわりに

Twitterを直接見るのに比べると色んな記事の流量は減ってると思う。知る機会は減ったかもしれないが、知らなくても良いことを知らずにすむようになったんではないかと思う。

ノイズが少ない方が自分としては嬉しいかもしれない。