maaash.jp

what I create

Ghost - build REST APIs from structs using Generics in Golang

Go 1.18 has been out for a while now. Are you using Generics?

At first, I was delighted, started gradually switching from map and filter-like for statements to github.com/samber/lo and I created an internal slice package to add some more variants.

However, somewhere in my heart, I felt frustration building up. I felt like I was missing out on something like I didn’t understand its true value because I hadn’t used it to its fullest. It is as if I have been given such an interesting toy and have not played with it to the fullest.

I want to write code using Generics!

I’ve been writing REST APIs in my daily job, and I’ve been thinking … isn’t this is the place in the conclusion of When To Use Generics:

If you find yourself writing the exact same code multiple times, where the only difference between the copies is that the code uses different types, Consider whether you can use a type parameter.

Aren’t we writing the same code many times?
Aren’t we writing REST APIs over and over again?
Isn’t it time to use Generics to build REST APIs?

I thought I’d give it a shot, and have experimented with Generics for a few days. The results are as follows.

Ghost - Build REST APIs from structs using Generics

The README of the package github.com/mash/ghost contains the following example.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
type User struct {
  Name string
}

type SearchQuery struct {
  Name string
}

func main() {
  store := ghost.NewMapStore(&User{}, SearchQuery{}, uint64(0))
  g := ghost.New(store)
    // g is a http.Handler and it provides GET /?name=:name, GET /:userid, POST /, PUT /:userid, DELETE /:userid
    ListenAndServe("127.0.0.1:8080", g)
}

Can you see the intent of this?
User type is a resource, that Ghost turns into an HTTP handler that provides POST, GET, PUT, and DELETE HTTP methods calls for CRUD operations.
SearchQuery type represents the search query for GET /.
User resources have an uint64 identifier, which constructs the path for the REST API.

When you hear the word “REST API,” you may have a variety of different styles in mind. The request and response bodies may be JSON, MessagePack or Protocol buffers etc. When returning an error, will it contain an error code and a human-readable msg or do you prefer “error_description”?

The constraints should be as loose as possible to allow for various styles of implementation. I tried to construct Ghost as a set of simple generic interfaces so that package users can implement their style and be able to swap and combine the generic interfaces to build the REST API.

Three generic types are used. In this example, a resource of type User is type Resource any, a list query of type SearchQuery is type Query any, and an identifier of type uint64 is type PKey interface { comparable } as the type constraint.

Here are some of my findings that I found interesting while writing this.

1. Generic interfaces to escape the generic world

One of my favorite design patterns when writing Go is the Middleware Design Pattern.

Middleware Design Pattern - from https://docs.pylonsproject.org/projects/pylons-webframework/en/latest/concepts.html#wsgi-middleware

By providing a standard interface that inserts operations before and after an operation at the center of an onion, simple-functional middlewares (layers of onions) can be combined to describe complex operations as a whole. We see this a lot in http.Handler. (PSGI/Plack was probably the first one that shocked me when I first saw it).

I thought it would be effective to apply the same pattern to REST API, so I defined the following generic interface.

1
2
3
4
5
6
7
type Store[R Resource, Q Query, P PKey] interface {
  Create(context.Context, *R) error
  Read(context.Context, P, *Q) (*R, error)
  Update(context.Context, P, *R) error
  Delete(context.Context, P) error
  List(context.Context, *Q) ([]R, error)
}

Let’s start with an obvious feature for APIs. Request parameters validation. I implemented this interface and used github.com/go-playground/validator to validate request parameters before CRUD operations, and that’s github.com/mash/ghost/store/validator. An excerpt is shown below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
type validatorStore[R ghost.Resource, Q ghost.Query, P ghost.PKey] struct {
  store ghost.Store[R, Q, P].
  validate *validator.Validate
}

func NewStore[R ghost.Resource, Q ghost.Query, P ghost.PKey](store ghost.Store[R, Q, P], validator *validator.Validate) ghost.Store[R, Q, P] {
  return validatorStore[R, Q, P]{
      store: store,
      validate: validator,
  }
}

func (s validatorStore[R, Q, P]) Create(ctx context.Context, r *R) error {
  if err := s.validate.StructCtx(ctx, r); err ! = nil {
      return validationError(err)
  }
  return s.store.Create(ctx, r)
}

Simple and good.

We often see cases when you just can’t express your validation rules in Golang tags. It would be nice to be able to call hooks before CRUD operations to implement custom validations. So I created hookStore.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
type hookStore[R Resource, Q Query, P PKey] struct {
  store Store Store[R, Q, P]
}

func NewHookStore[R Resource, Q Query, P PKey](store Store[R, Q, P]) Store[R, Q, P] {
  return hookStore[R, Q, P]{
      store: store,
  }
}

type BeforeCreate interface {
  BeforeCreate(context.Context) error
}

} type AfterCreate interface {
  AfterCreate(context.Context) error
}

func (s hookStore[R, Q, P]) Create(ctx context.Context, r *R) error {
  if h, ok := any(r). (BeforeCreate); ok {
      if err := h.BeforeCreate(ctx); err ! = nil {
          return err
      }
  }
  if err := s.store.Create(ctx, r); err ! = nil {
      return err
  }
  if h, ok := any(r). (AfterCreate); ok {
      if err := h.AfterCreate(ctx); err ! = nil {
          return err
      }
  }
  return nil
}

The argument r of generic type *R, can be cast to any®and then we can use type assertion to check if r implementsBeforeCreate,AfterCreate` interfaces. This is familiar, you can do this without generics.
But how about …

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
type BeforeList[Q Query] interface {
  BeforeList(context.Context, *Q) error
}

type AfterList[R Resource, Q Query] interface {
  AfterList(context.Context, *Q, []R) error
}

func (s hookStore[R, Q, P]) List(ctx context.Context, q *Q) ([]R, error) {
  var r R
  if h, ok := any(&r). (BeforeList[Q]); ok {
      if err := h.BeforeList(ctx, q); err ! = nil {
          return nil, err
      }
  }
  l, err := s.store.List(ctx, q)
  if err ! = nil {
      return l, err
  }
  if h, ok := any(&r). (AfterList[R, Q]); ok {
      if err := h.AfterList(ctx, q, l); err ! = nil {
          return nil, err
      }
  }
  return l, nil
}

I discovered that you can also type assertion to generic interfaces BeforeList[Q] and AfterList[R, Q] with R and Q as type parameters! It’s starting to look like something I haven’t seen anywhere else.

The code to implement these interfaces is in the test.

1
2
3
4
5
6
7
8
9
func (u *HookedUser) BeforeList(ctx context.Context, q *SearchQuery) error {
  globalCalled["BeforeList"]++ // record the call for testing
  return nil
}

func (u *HookedUser) AfterList(ctx context.Context, q *SearchQuery, rr []HookedUser) error {
  globalCalled["AfterList"]++
  return nil
}

This is interesting. I can call a function with a non-generic concrete type as an argument from within the generic world. To avoid typos and accidentally not implementing the interface, it is safe to write as follows.

1
2
var _ ghost.BeforeList[SearchQuery] = &HookedUser{}
var _ ghost.AfterList[HookedUser, SearchQuery] = &HookedUser{}

I also created gormStore using the OR mapper, gorm and applied the same mechanism to implement hooks. I wrote gormStore so that if the resource struct implements the generic interface, that overrides the generic implementation. Take a look if you are interested.

2. Specialization?

I have only written C like C++ and have avoided Java as much as possible, so my vocabulary for Generics is lacking. It’s hard to explain why I find this interesting, but let’s go ahead.

If you run the above example, you will get a path named /:userid, where the :userid part is a uint64. Since the path is a string, the string is converted to uint64 by strconv.ParseUint.

When you create a REST API, you may want to use a string (often called a slug) as an identifier, like the maaash part of twitter.com/maaash. However, there is no need to strconv.ParseUint a slug.

So I implemented each separately.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
type PKey interface {
  comparable
}

type PUintKey interface {
  comparable
  ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64
}

type PStrKey interface {
  comparable
  string
}

type Identifier[P PKey] interface {
  PKey(*http.Request) (P, error)
}

// PathIdentifier is an Identifier which extracts the PKey from the request URL path.
type PathIdentifier[P PKey] func(string) (P, error)

func (pi PathIdentifier[P]) PKey(r *http.Request) (P, error) {
  var p P
  _, lastpath := path.Split(r.URL.Path)
  if lastpath ! = "" {
      return pi(lastpath)
  }
  return p, ErrNotFound
}

func UintPath[P PUintKey](s string) (P, error) {
  i, err := strconv.ParseUint(s, 10, 64)
  return P(i), err
}

func StrPath[P PStrKey](s string) (P, error) {
  return P(s), nil
}

As I wrote in the beginning,

I tried to construct Ghost as a set of simple generic interfaces so that package users can implement their style and be able to swap and combine the generic interfaces to build the REST API.

Identifier is one such component, a generic interface to retrieve a PKey from an HTTP request. PathIdentifier is one of its generic implementations, which retrieves PKey from URL.Path of the HTTP request. UintPath and StrPath handle the case where the PKey is a uint or a string, respectively.

I find that fascinating that … The whole “ghost” uses a loose constraint that is type PKey interface { comparable }, while the PUintKey and PStrKey, which are stricter variants of PKey, are used to cover the most major use cases. If package users want to use their special type, they can implement the Identifier interface themselves, and it can be integrated into Ghost…

If you have reached this point, you may think I am overusing generics. I honestly agree too. Some may criticize that you can achieve the same with interfaces. Have you read the intro? The whole point of this article is to try to use the hammer. But on the other hand, what if there were more ready-made generic implementations of the components in Ghost?

I’m thinking of using it in a side project first.

I’m glad that I made Ghost because it relieved my frustration of not being able to fully touch Generics. I was worried that the inclusion of generics in Go 1.18 would make the code more difficult to read, but I’m grateful to the team for how it landed. Go is continuing to be really easy to read.

My biggest complaint about Generics in Go is … Github Copilot is not very helpful anymore. Seriously, if the accuracy of the code proposed by Copilot is poor, my productivity will decrease. Let’s all write public code using Generics and feed Copilot!

If you find it interesting, I will be glad if you give me feedback in the repository or @maaash](https://twitter.com/maaash).

This is an English translation of the original Japanese article that I wrote on Golang GenericsでREST APIを作る.

IRKitサービス終了しました

IRKitのサービスを終了しました。 2022-12-10追記


IRKitの販売開始は2014年1月15日。自分が最後の1台を販売したのが2017年11月25日でした。開始してから8年が過ぎましたか。

最終的には生産台数は 100+500+1000+1000+2000+2000+3200+3000 = 12800 台。

サーバに接続しているIRKitの数を見てもそれらを通して赤外線信号を送っている人の数を見ても、0ではありませんが、そろそろ良いかな、という頃合いです。

7ヶ月後の2022年11月末を目処にサーバを停止しサービスを終了します。

終了後、出荷時のファームウェアでお使いいただいている方は、IRKitのサーバを通して赤外線を送信することができなくなります。IRKitにはローカルネットワークのAPIがありますが、それは(工場出荷時のファームウェアでは)IRKitがサーバに接続している時に、ローカルネットワークからのレイテンシーを短くするために使うAPIで、サーバに接続できない場合には無効です。

幸いにもIRKitのファームウェアはオープンソースであり、この問題を解決してくれた方がいらっしゃいます。 こちらのpull request https://github.com/irkit/device/pull/6 を書き込むと useCloudControl という設定をfalseにしてビルドすることで、サーバに接続する機能を停止します。この時ローカルネットワークは使えます。 そもそも今のMacやWindows PCからIRKitのファームウェアを書き込むことができるのかなど困難はあるでしょうが、ご希望であればお試しください。

またIRKitはArduino Leonardoにインスパイアされて回路設計しました。IRKitとしては使わなくとも、Arduino Leonardoが手元に1台あるようなものです。日々の工作にうまく末長くお使いいただければと思います。

外出先、音声アシスタントから家電を操作したい方はNature Remoシリーズがおすすめです。よろしければ。

これまでどうもありがとうございました。感泣。

Natureに復帰しました

Nature社に1エンジニアとして復帰して1ヶ月が経った。

次どこで仕事をしようか、しばらく考えていた。エストニアに引っ越した時にも書いたが復習しながらcriteriaを挙げると、

  1. 小さく柔軟な会社

    これまで勤めた体験から、自分の好みがわかってきている。 少ない人数で議論して決められる、時間をかけて正解を求めるよりはiterativeに前進する、歯車というよりはメンバーが自分の仕事の範囲を広めに捉えオーナーシップを持っている、社内の上司を説得するのではなくお客様に向きあっている。 そういうチームで働くのが好きなようです。

  2. 何かしら世界を良くしようとするビジョンとロジックがあること

    自分のお金を使う、自分の時間を使うときに、ただその対価を得るよりは、お金や時間を払う対象を応援しているからこそその対象を選択するという考えが強くなってきている。 そして子供ができてから、自分の時間を使う、仕事をすることで特に地球環境の面でより良い世界を子供達に渡したいという考えも強くなってきている。

    「ロジックがあること」が大事。

  3. 当たり前だが報酬が良い

    Equityもね。

  4. もちろん尊敬できるチームがいること

  5. エストニアに引き続き住んでいるのでここから働けること

対して自分が提供できる価値は、主にソフトウェア開発(電気やメカの開発もIRKitでやったけど基本的にはソフトウェアエンジニアだよね)、プロダクトの開発、事業の開発。

自分が提供できる価値と1~5の掛け算で考えると、あんまりないのでは???特に2。環境分野の事業でソフトウェアエンジニアが貢献できるところ。。。3,4,5も含めると、少ないですね。。

Nature社に復帰して1ヶ月、スタートアップでは当たり前のこともあるだろうし「そんなこと?」という点もあるだろうが前職があれだったのか基本的なことがとても嬉しい。

  • ミーティングがデフォルト30分。
  • 時間通り始まる。
  • 議事録がある。
  • Slackチャンネルが基本的にpublicである。
  • 事業の数字が待っていても入ってくる状態にある(もちろん求めれば増えたりする)。
  • ツールによる情報のflow/stockの違いが認識されている。
  • イギリスやドイツで働いている人がいるなどリモートで仕事しやすい感じがある。
  • 心理的安全性を感じる。
  • コミュニケーション能力が高い人が多い気がする。
  • カスタマーサポートのメンバーが開発プロセスに入っている。
  • 自動化することが普通である。
  • 好きな発電所ありますか?って質問で盛り上がれるww

このあたりを魅力的に感じる方がいらっしゃいましたら、ご連絡ください。 @maaash Nature社では絶賛エンジニア募集中です。

Nature Remoシリーズについても、ご要望や応援メッセージ、直接ご意見いただくのも嬉しいタイプです。 @maaash へどうぞ。

BambooRollとカーボンフットプリント

次このブログに書く、「つくったもの」がトイレットペーパーとは思わなかったでしょう?

BambooRoll - 竹でつくったトイレットペーパーの定期便

そこにBambooRollのカーボンフットプリントを考察するという記事を書いた。
ここの余談を拡張する。

カーボンフットプリント、つまり製品のライフサイクルで排出される温室効果ガスをCO2量で表したもの
が面白い!

カーボンフットプリントの算出は以下のようにして行う。製品のライフサイクルで様々なリソースを使用する。そのリソースについて、ある単位のリソースを使うとCO2に換算すると何kg排出しているか、予め算出してある二酸化炭素排出量原単位(以降、CO2排出原単位)を参照する。このデータベースには4)を使用する。使用したリソースの量に、CO2排出原単位をかけると、CO2排出量が求まる。製品のライフサイクルで使用するリソースについて、CO2排出量を求め積算していく。

この4)はカーボンフットプリント制度試行事業CO2換算量共通原単位データベースver. 4.0 (国内データ)

例えばこうやって身近なモノや作業の換算CO2量を計算できる。
水と給湯の原単位から…

スクリーンショット 2021-01-18 7.26.42
スクリーンショット 2021-01-18 7.27.17

お風呂につかるという行為の換算CO2量は以下のように計算できる。

お風呂の水の量を200L、温度を40度、水道水の温度を年間平均の16度として、
水は 0.2 [m3] x 3.48E-1 [kg-CO2e/m3] = 0.0696 [kg-CO2e]
ガスは (40-16) [C] x 200 [L] x 3.39E-4 [kg-CO2e/C*L] = 1.6272 [kg-CO2e]
合わせて 1.6968 [kg-CO2e]

次は、、焼肉。 牛と米と電気から…

スクリーンショット 2021-01-18 7.23.46
スクリーンショット 2021-01-18 8.37.28
スクリーンショット 2021-01-18 8.46.57

ホットプレートで焼肉を焼いて食べるという行為の換算CO2量は

肉300gを1時間焼く、米200gを1時間炊く、ホットプレート1300W、家庭用省エネ性能カタログより炊飯時の消費電力は158Wh/回として、
牛肉は 0.3 [kg] x 9.21 [kg-CO2e/kg] = 2.763 [kg-CO2e]
ホットプレートは 1.3 [kW] x 1 [h] x 4.79E-01 [kg-CO2e/kWh] = 0.6227 [kg-CO2e]
米は 0.2 [kg] x 1.59 [kg-CO2e/kg] = 0.318 [kg-CO2e]
炊飯器では 0.158 [kWh] x 4.79E-01 [kg-CO2e/kWh] = 0.075 [kg-CO2e]
合わせて 3.7787 [kg-CO2e]

ふむふむ。牛肉、ガスかあ。。

ちなみに家庭用の車は運輸部門における二酸化炭素排出量より
0.133 [kg-CO2/人km]

精度が高くはないだろう、そもそもこの原単位表の最初の注意事項参照、ブログの記事を書く上では原単位にどこまで含んでいるのか確認していない、ホットプレートを1時間フルでつけないだろう、スーパーから買ってくる時の移動に車を使ったら、、などなど。
でも家計簿をつけたり、家電の消費電力をグラフ化すると得られる納得感のようなものがある。

単位を変換していく作業自体にはパズルのような面白さがあるように感じる。 光速を導入することで距離の単位がmから時間に変わることを思い出した。

部品の原単位と使用量から製品の換算CO2量を積み上げていくことができて、それを再帰的に繰り返すことで適用範囲を広げていく、というのはインターネットの何かに近くて面白い。
企業が製品のカーボンフットプリントを公開していく世界になったら良いと思う。
カーボンフットプリントコミュニケーションプログラムには期待。

間違いがあれば識者の方ご指摘お願いします。


まとめ

計測厨の心をくすぐるような、パズルのような、再帰的な、面白さがカーボンフットプリントにはある。

トイレットペーパーはあなたも使っているでしょう?
BambooRollをお試しください。

Uncomment JSON or using JSON5

最近あるプロジェクトで階層構造のある設定ファイルが必要になり、コメントを入れる必要からYAMLフォーマットを選択した。 だがYAMLはoctalの罠にハマった嫌な思い出がある。

Integer Language-Independent Type for YAML™ Version 1.1

1
2
3
4
5
6
7
Valid values must match the following regular expression, which may also be used for implicit tag resolution:
 
 [-+]?0b[0-1_]+ # (base 2)
|[-+]?0[0-7_]+ # (base 8)
|[-+]?(0|[1-9][0-9_]*) # (base 10)
|[-+]?0x[0-9a-fA-F_]+ # (base 16)
|[-+]?[1-9][0-9_]*(:[0-5]?[0-9])+ # (base 60)

0で始まり7までの数字だけであれば base 8 になり8以上の数字を含めば base 10 になるのは無しだろう。

YAML 1.2ではこの罠は無いが

それにどこかで読んだ、設定ファイルが途中で途切れたままデプロイされても文法エラーにならない、 という欠点は気になる。

コメントさえ使えればJSON使うのに… ということでシンプルなツールを書いた。

Uncomment | github.com/mash/uncomment

良いライブラリがあったので特に難しいことはしていない。 コマンドの引数やstdin,stdoutが他のツールと組み合わせやすいか、だけ気にして作った。

元々のあるプロジェクトではviperを使っていたので何も変えずに設定ファイルの食わせ方を変えるだけでコメントを含んだ緩いJSONを使えるようになる。

1
2
3
4
5
# パイプはよしなに扱う
% cat relaxed.json | uncomment | jq .

# -oでファイル名を指定するとstdoutにファイル名を書く
% command -c `uncomment -i relaxed.json -o strict.json`

ライブラリ選定中にJSON5というのを発見して、「これジャン!」となり、こりゃ欲しいツールは既にあるだろうな心のどこかで思いながらも最後まで走りきって作ってしまった。 案の定似たようなツールはたくさんあった

この小さなプロジェクトの気に入っている点は、良い名前をつけられたことかな。 パイプで繋いだ時の見え方、しっくりくるではないか。

BLE-Proximity for COVID-19 contact tracing

コロナウィルスが広まり始めてから、自分の専門性を生かして何かできないかと考えていた。

Bluetooth Low Energyを使ったスマートフォンアプリを配布し、誰と誰が近くにいたか履歴をとっておいて、後にアプリのユーザーの中から感染が確認されたら、そのユーザーが近くにいた他のユーザーに警告する、
というアイディアがあることを知ったのは 鐵人三國誌 だったか。

これは得意領域やん、と思い飛びつき、
コロナのおかげで自分も時間が多少できたので(!?)
そこからシンガポール/TraceTogetherヨーロッパ/Decentralized Privacy-Preserving Proximity Tracingで検討/実装されているアプリ Projects using personal data to combat SARS-CoV-2 をリサーチをしつつ、プライバシーを保護して、どうBLEを使えばBackgroundでしか使わないだろうこのアプリ間の通信が可能で、Androidも共存できて、小さく初めてすぐにリリースできて、アプリユーザーが増えてもサーバ負荷が軽そうな仕組みを考えていた。

そして昨日 Privacy-Preserving Contact Tracing を発見するまで一週間ばかり。

Apple, Googleが5月に出すならそれを待とう、という空気だと思うので、この一週間ばりばり書いていたコードを公開して 供養したい

BLE-Proximity / BLE-Proximity implements contact tracing on iOS, to prevent COVID-19 transmissions.

このプロジェクトは何もなければフリーズしますが、何か良い利用方法があれば協力したいです。右の連絡先にご連絡ください。


考えていたアプリの仕組みは(英語で)レポジトリのREADMEに書いたがここでは日本語で。

要約すると、

  • UInt64のランダムな数をアプリ内で生成してuserIdとする
  • Bluetooth Low Energyでこれを交換する
  • 定期的にuserIdは更新し、アプリ内に自分のuserIdの履歴を保存する
  • BLEで受け取った相手のuserId(peerIdとする)も履歴を保存する
  • 履歴は4週間まで保存し古いものは捨てる
  • COVID-19 positiveになった人は、自分のuserIdの履歴を、同意の後、(ここはTODOだったが)医者の電子署名(ができる国だったらよかったな〜)など信頼性を保証しつつサーバへ保存
  • サーバではpositiveだった人のuserIdの履歴を公開
  • アプリはpositive userIdリストをダウンロードして、アプリ内のpeerIdの履歴にマッチするものが無いか探す

という感じ。

サーバ側の開発が軽い、プライバシー面が安心(サーバに送る情報がpositiveでなければ何もない, BLEを通して送るuserIdもランダムだし更新する)、という意味で筋が良いと考えていた。

そしてこのアプリの機能が広く多くの人に使ってもらえることを考えると、LINEやメルカリなど多くのユーザーがダウンロードしているアプリのアップデートとして使ってもらうのが最高だろう(もちろん自分の意思でオプトインしてね)。ということを考えてFrameworkとアプリを分けてFramework側にこの機能を実装していた。

https://www.apple.com/covid19/contacttracing から辿れるApple, Googleのドラフト技術資料を見ると大きな違いは、

1. BLEではAdvertisingにデータをのっける

そりゃできたらそうするわ!
iOSではAdvertising dataにアプリが(GATT ServiceのUUID以外の)任意のデータをのせられる公開APIは無い。

First, in May, both companies will release APIs that enable interoperability between Android and iOS devices using apps from public health authorities. These official apps will be available for users to download via their respective app stores.

https://www.apple.com/newsroom/2020/04/apple-and-google-partner-on-covid-19-contact-tracing-technology/

これ英語が意味不明なのだが、”both companies will release APIs” なのか “download via their respective app stores” なのかどっちなのか。
Advertising dataをアプリからいじれてそれが非公開APIとしてiOS13.xからできるようになり、その後公開されていくとしたら熱いがそれは無いかなあ…

これができないので、BLE-Proximityでは、read characteristic, write characteristicを用意し、BLEでuserIdを読み書きできるようにした。AdvertisingしているiOSアプリをScanで見つけられてread/writeできない、ほど一時的に近くにいる場合は無視してよいでしょう。それにread, write characteristicを両方持っていれば、BackgroundでAdvertiseしているiOSアプリを2つ考えた時に、それぞれのアプリが相手のAdvertiseをScanして発見しなくてよい(片方だけでよい)ので発見率が上がるはず。

iBeaconを使えば、というオプションもあった。
iOSアプリでiBeaconのふりをすることは可能だが、バックグラウンドでは継続してiBeaconとしてAdvertiseできない & iBeaconのAdvertiseにアプリからのせられる情報はMajorとMinorの合計32bitで、それだとuserId空間が足りない。

という理由から却下した。このアプリをForegroundで使うことは考えにくいので、iOSアプリ間で、バックグラウンドで通信できないのはきつい。

もちろんiBeaconでなくてもBackgroundのAdvertiseはForegroundに比べ見つかりにくいので..ではあるが

2. userIdの生成が違う

Apple, Googleのドキュメントでは、userIdに相当するものは Tracing Key –> Daily Tracing Key –> Rolling Proximity Identifier と生成され最後のが交換される。 positiveな人がいたらその人のDaily Tracing Keyに相当するものがサーバにアップされる。

こうすると、サーバ上にアップする情報は、Daily Tracing Key x その人がウィルスを持っていた期間の日数になり、Rolling Proximity Identifierはもっと頻繁に変更できる。

BluetoothのMACアドレスの更新と同期するというのもなるほど。BluetoothのMACアドレスの更新タイミングってiOSアプリで公開APIから知れるんだっけ?

なるほどHKDF便利やん…


ということで、何かを作ってみると他の人が作ったものがいかによくできているか痛感できてさらに勉強になる、という良い例であったのと、

プラットフォーム側にたつのは自由度が高く楽しいだろうなあ、

このアイディアもっと早くに思いつけていたら違ったはずだ、がんばれ。

とまとめてしまいとする。

ラムネのあれ

ラムネのあれを3Dプリンタでつくった。

Untitled

Untitled

「いつものより開けやすい」と好評です。

エストニアに引っ越した

エストニアの首都タリンに引っ越してから5ヶ月たった。

Untitled

タリン旧市街

2013年にカヤックをやめた理由の1つがそれだったので、もう5年もずうっと海外に引っ越したいと考えていてやっと叶った。5年もかかったのは条件が厳しかったからだろう。

仕事面では、

  • 小さく柔軟な会社
  • 何かしら世界を良くしようとするビジョンとロジックがあること

場所に対しては

  • 英語教育がちゃんとしていること
  • 安全であること

この両立。

自分がウェブやスマホアプリの開発をやってきていたので、「やっぱりベイエリアに行って本場にいたい」と思っていたが、ビザ面でうまくいかなかった。

アメリカの就労ビザを小さい会社がサポートするのは厳しい。
H1Bは抽選だし、抽選に通っても給与の下限が賃金1000万円以上という条件があるので会社が小さすぎるときつい。
Eビザもあるが、基本的には「アメリカで人を採用してくれるんだろうな?」ということなのでその計画がたっていないときつい。

1年日本で仕事してLビザでアメリカへ行くのが手っ取り早そうだが、やはり1,2番目の条件に引っかかり踏み出せなかった。
グリーンカード抽選が当たるほどラックにふられている設定ではなかったようだった。

そうこうしているうちにトランプが大統領になり、アメリカでは学校での発砲事件が引き続き問題になっていて、
「おいおいアメリカでいいのか?」と思うようになってきた。
YouTubeでなんでもいいからトランプの演説を見てみるといい、このおっさんが大統領になるようではだめだ。

逆にエストニア移住につながるきっかけがあり、エストニアになびいていった。
エストニア移住プランは上記の条件を満たしていた。

エストニアについて調べるとどんどん気になっていった。

  • E-Governmentの分野で先進的である
    E-Residencyというのが始まった。

  • 首都タリンの人口が40万人程度で小さい
    日本の藤沢市と同じくらいか、、と知ると急に親しみが湧いた。
    小さな島のようなコミュニティはむかしから好きだった。

  • 寒い
    札幌くらいか、、と知ると、スキーヤーなので(エストニアにはマウンテンスキーする山は無いが)雪が降る街か、、と急に楽しみになった。

  • エストニアの大統領
    女性で46歳。
    トランプは72歳、安倍首相は63歳。

  • 見学に行った
    英語で授業をしている小学校を見て、町並みを見て、誘ってくれた会社のメンバーに会った。

ある時、もしかしたらエストニアは国レベルでスタートアップなのではないか?という考え方が生まれた(Exitはないから中小企業かもしれないが)。

20万人以上社員のいる大企業(P)と、個人事業や2人創業者のスタートアップで働いた経験を、
日本やアメリカとエストニアに照らしてみると、国に対しても会社選びと同じような嗜好で選べばいいんじゃないか、

社員数 / 国の人口
若いCEO / 若い大統領
会社のプロダクト / 国の施策

若いリーダーが率いる小さな国のメンバーになる、というのはおもしろい体験なのではないか?
そういうリーダーを選べる国民がいる、というのはおもしろいんじゃないか?
と思うようになってきた。

中略…

まとめ

タリンに引っ越しました。

SR-90サーボホーンのギザギザ付きをつくった

SG-90 servo horn

サーボを使った何かをつくっている。
最も手に入りやすく安い SG-90 をいくつか買って、Arduino Unoから角度を変えてみたりしていると、付属のサーボホーンでは物足りなくなくなってきたので作ってみた。

SG-90 near shot

このようにサーボの先端には細かなギザギザがついていて、付属のサーボホーンはこのギザギザにマッチするギザギザが穴の内側にあるため、ネジを使用しなくてもサーボの先端に固定されてくれる。

先駆者である Modelling a Servo Spline を参考にしつつ、
Fusion360でこんな感じで2等辺三角形を21個、円周上に繰り返すと敷き詰められる。
contraintに式を使うと連動して全体が変わるのがおもしろい。

Fusion360 constraints for SG-90 horn

2等辺三角形の1辺の長さを変えると、円周上に並んだギザギザの高さが変わるので、
これを3Dプリンタで出力してSG-90にはめてみては
また2等辺三角形の1辺の長さを変えて、と3回繰り返したらいい感じにはまるのができた。

IMG_1461

Afinia H480, ABSを使用してうまくいったのが以下のファイル。

SG-90

Fusion360 archiveでダウンロードすると取り込んでconstraintを編集できそう。
3Dプリンタや樹脂の素材で仕上がりが異なるだろうと思うので、2等辺三角形をご自由にいじったり、(そもそも穴がまだ空いてないので)ご自由にあけて使ってください。
共有URLができるのはFusion360素晴らしいな!

SG-90 servo horn with gears on thingiverse

Prototyping Lab 第2版にIRKitを取り上げていただきました

Prototyping Lab 第二版の作品紹介にIRKitを取り上げていただきました!

Untitled

IRKitをなぜ作ろうと思ったのかから、IRKitができあがっていくまでの軌跡がまとまっています。

Orphe, Mesh, HACKberry, ベゼリーなどの作品紹介も楽しい。

Orpheの、LEDストリップをサイドから底面向きに変更するエピソードは、一気に問題がいくつも解決するアイディアでありながら新たな問題が複数発生したんだろうなと想像できて熱くなる。 こういった、ハードウェア製品を作る際に立ちはだかった問題とこうやって解決した、っていうストーリーをもっと読みたいんだけれど、良い方法は無いものか。

本はその後、入力(センサ)、出力、データ処理、、とArduinoのコードを交えてプロトタイプの作り方を紹介していくが、コードを読み書きせずに各レシピの概要を読むだけでも価値がある気がする。 ハードウェアを作るようになって実現できることの可能性が無限大とは思うものの、発想するアイディアの範囲は知識に限定されるので、知識をどう広げていくかが重要。

それに、以下のように、

  • センサの原理からくる得意/不得意な環境
  • センサのキャリブレーション
  • ノイズを含む入力にかけるフィルタ
  • ヒステリシス
  • 状態遷移

(いちいち説明するのがめんどくさい)エンジニアリングの基礎的な知識がまとまっていて、 エンジニアと会話できない非エンジニアにそっと渡したい、共通語彙集としても良い一冊かも。