Monads

Scala with Catsを読んでいく

What is a Monad?

ざっくり言うと、MonadはコンストラクタとflatMapメソッドを持つもの。

Option[A] flatMap (A => Option[B]) => Option[B]

すべてのMonadFunctorでもある。

flatMapmapメソッドを持っていれば、for-comprehension(For式)を使うことができる。

Monad Laws

  • Left identity
    • pure(a).flatMap(func) == func(a)
  • Right identity
    • m.flatMap(pure) == m
  • Associativity
    • m.flatMap(f).flatMap(g) == m.flatMap(x => f(x).flatMap(g))

ここらへんの圏論用語がいまいち分からない。

  • identity ... 恒等射
  • associativity ... 結合律

Monads in Cats

cats.Monadは2つの型クラスを継承している。

  • FlatMap type class
    • flatMapメソッドを提供している
  • Applicative
    • pureメソッドを提供している
    • ApplicativeはFunctorを継承しているため、すべてのMonadでmapメソッドを使うことができる

Error Handling

プログラムで発生する可能性のあるエラーを表す代数的データ型を用いたアプローチ

sealed trait LoginError extends Product with Serializeable

final case class UserNotFound(username: String) extends LoginError

final case class PasswordIncorrect(username: String) extends LoginError

case object UnexpectedError extends LoginError

case class User(username: String, password: String)

type LoginResult = Either[LoginError, User]

def handleError(error: LoginError): Unit =
  error match {
    case UserNotFound(u) => println(s"User not found: $u")
    case PasswordIncorrect(u) => println(s"Password Incorrect: $u")
    case UnexpectedError => println(s"Unexpected error")
  }

Aside: Error Handling and MonadError

CatsはMonadErrorと呼ばれるエラーハンドリングのために使われるEitherのような抽象化したデータ型を提供する。

package cats

trait MonadError[F[_], E] extends ApplicativeError[F, E] with Monad[F] {

   def raiseError[A](e: E): F[A]

  def handleErrorWith[A](fa: F[A])(f: E => F[A]): F[A]

  def handleError[A](fa: F[A])(f: E => A): F[A]

  def ensure[A](fa: F[A])(f: A => Boolean): F[A]
}
  • F[_] はMonad型
  • EはFに含まれるエラー型

MonadErrorの重要なメソッドは以下の2つ

  • raiseError
  • handleError

raiseErrorはMonadでいうpureメソッドに近い。

handleErrorWithraiseErrorを補足する。

The Eval Monad

cats.Eval は、評価の様々なモデルを抽象化するためのMonadである。

典型的な2つはeagerlazyでそれぞれcall-by-valuecall-by-nameとも呼ばれる。

Evalはまた結果をメモすることができ、call-by-needで評価もできる。

ja.wikipedia.org

また、Evalはスタックセーフなのでとても深い再帰処理でもスタックを開放することなく使用できる

評価時の挙動

call-by-value evaluation

val x = {
  println("Computing X")
  math.random
}
// Computing X
// val x: Double = 0.0056134621561709785

x // first access
// val res0: Double = 0.0056134621561709785 // first access

x // second access
// val res1: Double = 0.0056134621561709785
  • 定義した時点(eager)で計算が評価される
  • 計算は一度だけ評価されメモ化される

call-by-name evaluation

def y = {
  println("Computation Y")
  math.random
}
// def y: Double

y // first access
// Computation Y
// val res0: Double = 0.6768399768604834

y // second access
// Computation Y
// val res1: Double = 0.1400800525943975
  • 計算は使用した時点(lazy)で評価される
  • 計算は毎回使用されるたびに評価される(メモ化されない)

call-by-need evaluation

lazy val z = {
  println("Computation Z")
  math.random
 }
// lazy val z: Double

z // first access
// Computation Z
// val res0: Double = 0.5971338589578261

z // second access
val res5: Double = 0.5971338589578261
  • 定義時点では評価されず(not eager)、使用した時点で評価される(lazy)
  • 一度評価されると結果はキャッシュされる(memoized)

Eval's Models of Evaluation

Evalには3つのサブタイプがあり、上記で記載した評価時の挙動と一致している。

  • Now(call-by-value)
  • Always(call-by-name)
  • Later(call-by-need)
Scala Cats Properties
val Now eager, memoized
def Always lazy, not memoized
lazy Later lazy, memoized

The Writer Monad

cats.data.Writerは計算と一緒にログを運ぶことができるMonadである。

Writer[W, A]は2つの型を運ぶことができる。

Wはログの型でAが結果の型を表す。

The Reader Monad

cats.data.Readerは入力に依存する操作を連続させることができるMonadである。

Readerのインスタンスは引数一つの関数をラップし、それらを合成するための便利なメソッドを提供する。

import cats.data.Reader

final case class Cat(name: String, favoriteFood: String)

val catName: Reader[Cat, String] = Reader(cat => cat.name)

catName.run(Cat("Steve", "tuna"))
// res1: cats.ppackage.Id[String] = "Steve"

The State Monad

cats.data.Stateは計算の一部として追加のステートを渡すことができる。

アトミックな状態操作を表すStateを定義し、mapやflatMapを使ってそれらを繋ぎ合わせる。

State[S, A]のインスタンスは S => (S, A)の型を持つ関数を表す。

SがStateの型で、Aが結果の型。

import cats.data.State

val a = State[Int, String] { state =>
  (state, s"The state is $state")
}

val (state, result) = a.run(10).value
// state: Int = 10
// result: String = "The state is 10"

val justTheState = a.runS(10).value
// justTheState: Int = 10

val justTheResult = a.runA(10).value
// justTheResult: String = "The state is 10"

Stateの特徴は2つ

  • inputの状態をoutputの状態に変化させる
  • 結果を計算する

Stateは3つのメソッドを提供する

  • run
  • runS
  • runA

Define Custom Monads

以下の3つのメソッドを実装すればカスタムタイプのMonadを定義できる。

  • flatMap
  • pure
  • tailRecM

tailRecMメソッドはCatsで使われる最適化で、flatMapをネストして呼び出したことで消費されるスタックのスペースを制限するために使われる。

Monadに関する参考情報

MonadTransformer とは何か · GitHub

合成できるモナド、モナドが合成できる時 - Milestones to EVERPEACE 〜alius via〜

Functors

引き続き、Scala with Catsを読んでいく。

Functors

Functorとはざっくり言うと、map メソッドを持ったものと言える。

通常、Listsの反復処理でmapメソッドに出会うが、Functorを理解するには別の視点で考える必要がある。

Listsを走査する」と考えるのではなく、「すべての値を一度に変換する」と考えるべきである。

ある関数を適用するとすべての値に適用され値は変化するがListsの構造は保ったままである。

List(1, 2, 3).map(n => n + 1)
// List(2, 3, 4)

Future

Futureは非同期計算をキューイングしてシーケンス化し、前の計算が完了した時点で適用するFunctorである。

Futureは内部の状態については保証せず、Futureでラップされた計算は、ongoing(進行中), complete(完了), rejected(拒否)で表される。

もし、Futureが完了した場合は、マッピングされたカンスは即座に呼ばれる。もしそうでない場合は、下位のスレッドプールが関数の呼び出しをキューに入れて後で呼び出される。

Futureにおいて、「関数がいつ呼び出されるかは分からない」が、「関数がどの順序で呼び出される」かは分かる。

Note: Futureはreferentially transparent(参照透過性)でない 。

Futureは常に計算結果をキャッシュするため、これらの動作を調整することはできない。

Definition of a Functor

Functorの型は F[A]で、map操作は (A => B) => F[B]

Functor Laws

Functorは、多くの小さな操作を一つずつ順番に行う場合でも、それらを組み合わせて大きな関数にしてからマッピングする場合でも同じセマンティクスを保証する。

Identity: identity関数でmapを呼び出しても、何もしていない場合と同じである

fa.map(a => a) == fa

Composition: fgという2つの関数をマッピングすることはfという関数をマッピングしてからgという関数をマッピングするのと同じである

fa.map(g(f(_))) == fa.map(f).map(g)

Functors in Cats

Functorの型クラスは cats.Functorである。

Functorはliftというメソッドを提供していて、これは A => B という型の関数をFunctor上で操作し、F[A] => F[B]という型に変換する関数。

val func = (x: Int) => x + 1
val liftedFunc = Functor[Option].lift(func)
liftedFunc(Option(1))
// Some(2)

Exercise: Branching out with Functors

2分木型のFunctorを書け、という問題。

以下は、2分木の定義。

sealed trait Tree[+A]
final case class Branch[A](left: Tree[A], right: Tree[A]) extends Tree[A]
final case class Leaf[A](value: A) extends Tree[A]

Functorの実装はすぐ書けた。

  
  implicit def binaryTreeFunctor: Functor[Tree] = new Functor[Tree] {
    override def map[A, B](fa: Tree[A])(f: A => B): Tree[B] =
      fa match {
        case Branch(left, right) => Branch(map(left)(f), map(right)(f))
        case Leaf(value) => Leaf(f(value))
      }
  }

これを実行してみようと以下のように書いてみると、Cannot resolve symbol map だと怒られた。

  println(Branch(Leaf(1), Branch(Leaf(2), Leaf(3))).map(_ * 2))

Cats.Functorの定義を見てみると以下のようになっており、Invariant[F]を継承している。

trait Functor[F[_]] extends Invariant[F] { self =>
  def map[A, B](fa: F[A])(f: A => B): F[B]
  ...
}

ということは、変位指定は不変なので、Tree型を継承したBranch型やLeaf型は渡せない。

なので、Treeオブジェクトを作成して、Branch型とLeaf型のコンストラクターを作成する。

object Tree {
  def branch[A](left: Tree[A], right: Tree[A]): Tree[A] =
    Branch(left, right)

  def leaf[A](value: A): Tree[A] = Leaf(value)
}

これらのコンストラクターの返り値は Tree[A] なので、怒られずに実行できるはず。

println(Tree.branch(Tree.leaf(1), Tree.branch(Tree.leaf(2), Tree.leaf(3))).map(_ * 2))
// Branch(Leaf(2),Branch(Leaf(4),Leaf(6)))

実行できた。

Contravariant Functors and the contramap Method

Contravariant Functorcontramapというメソッドを提供する。

このcontramapメソッドは変換を表すデータ型にのみ意味がある。

F[B] contramap (A => B) => F[A]

Invariant functors and the imap method

Invariant Functorimapメソッドを実装しており、これはざっくり言うとmapcontramapの組み合わせと同じ。

F[A] imap (A => B, B => A) => F[B]

Monoids and Semigroups

お久しぶりです。一週間ちょっと前にコロナに感染してから常に身体が怠くて頭も重いから仕事でもプライベートでも何もやる気になりません。

ただ、このままずっとベッドの上で寝ていても良くないので、Scala with Catsを読み始めました。

www.scalawithcats.com

備忘録としてまとめておきます。

Monoid

型AのMonoidは以下の特性?を持ちます。

  • (A, A) => A の組み合わせの操作: combine
  • Aの空要素: empty

Scalaのコードで表すと以下の通り。

trait Monoid[A] {
  def combile(x: A, y: A): A
  def empty: A
}

Monoidにはいくつかの法則があります。

  • combineAssociative でなければならない
  • emptyIdentity Element でなければならない

Associative / Identity Element とは、具体的には以下のような法則です。

def associativeLaw[A](x: A, y: A, z: A)(implicit m: Monoid[A]): Boolean = {
  m.combine(x, m.combine(y, z)) == m.combine(m.combine(x, y), z)
}

def identityLaw[A](x: A)(implicit m: Monoid[A]): Boolean = {
  (m.combine(x, m.empty) == x) && (m.combine(m.empty, x) == x)
}

Semigroup

Semigroup(圏論で半群と呼ぶらしい)は、Monoidの結合部分(combine)のことを指します。

CatsのMonoidは以下のように定義されています。

trait Semigroup[A] {
  def combine(x: A, y: A): A
}

trait Monoid[A] extends Semigroup[A] {
  def empty: A
}

Duolingo English Testを受けてみた

会社で英語を使う必要が出てきたり、もっと洋書をスラスラ読みたい、英語圏で働くことも視野に入れられるようにしたいというモチベーションが出てきたので、本腰入れて英語学習をし始めました。

そこでまずは自分の実力はどれぐらいなのかを測定する必要があると思い、今回初めて「Duolingo English Test」を受けてみました。

このテストは語学学習サービスを開発しているDuolingoが開発したもので、多くの海外の大学やカレッジ、高校の入学選考書類として受け付けられるほどしっかりとしたものなので、気になっている人がいればぜひ試してもらいたいです!

englishtest.duolingo.com

何が良い?

お手軽

とにかくオンラインで手軽にいつでもテストを受けられるのが良いです!

TOEICだと申し込みの受付日程が決まっているし、実際に受験会場に足を運ばないといけないのがかなり億劫です。

でも、Duolingo English Testならオンラインでチケットを買って、いつでもテストを受けられるのですごく楽なのです!

テストの所要時間が短い

Duolingo English Testなら約60分程度で終わります。

これなら平日でも休日でもサクッと受けることができるので、休日が半日テストで潰れるなんてことがないのが嬉しいところです!

実践的な英語力を測れる

Reading, Listening, Writing, Speakingすべてが求められる問題になっています。

画像について英単語50語以上で説明、あることに対してのあなたは賛成するか反対するかの意見をスピーキングする等、個人的にこういうテストはあまり受けたことがなかったので新鮮でした!

感想

スコアは90でギリギリ中上級なんですが、見てもらうと分かるようにProduction(書いて話す能力:発信力)が著しく低いです。

今後は英語を書く・話すことを重点的に勉強しつつ、スコアを120ぐらいまで伸ばせるように毎日勉強していこうと思います。

certs.duolingo.com

blog.duolingo.com

転職して3ヶ月経った

2022年1月に今いる会社に転職して、もう3ヶ月経った。

今いる部署は技術研究をもとにプロダクトを開発することが多いので、暇なときは暇で忙しいときは忙しいらしい。

が、最初の2週間ぐらいは様子見で細かいタスクを振ってもらったが、それ以降すぐに既存アプリケーションのリアーキテクトプロジェクトに参加させてもらえたり、現在はそれと並行で新しいプロジェクトの管理画面を全部任せるね!って言ってもらえたりでやることが多いのでめちゃめちゃ楽しい!

既存アプリケーションに関しては、性能の事情から部分的にRustを使ってみようか!ということになり、CQRSアーキテクチャパターンで作られたアプリケーションのReadModelUpdater部分をRustで実装するタスクを任せてもらった。

Rustに関してはチュートリアルを軽くやったことしかなかったので、最初はとにかく書いてもコンパイルが通らない、どうやって書けばコンパイルが通るのか本当に分からずで苦労した(今もすんなりコンパイルは通せないから何回も書き直したりするけど、前よりマシだと思う)

また、非同期でメッセージの処理をしたり、Graceful Shutdownの実装のためにtokioを使ってごにょごにょやっていて、これも型の問題でいろいろ苦労した。

あと、そもそものモデリング力不足や実装力の問題で丁寧にレビューもらったりした(上長には感謝しかない🙏)

前職でコードを書いたり本読んで学んでいたつもりだったけど、環境に甘えすぎていた部分があったなと反省している。

そのあとは、Rust製のGraphQLサーバーを一部実装・修正をした。

そもそもRESTful APIしか触ってこなかったので、またここでも学び必須モードになり、ひたすらRustのライブラリであるasync-graphqlを調べたり、そもそもGraphQLってどんなもんなんだというのを調べたりしながら、実装・修正をしていた。

未だにGraphQLに関しては理解は浅いと思うし、エラーハンドリングとかのベストプラクティスってどうなのよ!っていうのはある。

新しいプロジェクトがそろそろ始まるよって段階で、上長とプロジェクト全体像のブレストしたりイベントストーミングしたりドメインイベントの発生源探したり、こういうモデルが必要だよねみたいな話をしたりして、このような取り組みはかなり意味のある時間だし、時間取って付き合ってもらってありがたいなと感謝している。

戦術的DDDで書かれたプロジェクトの機能追加、修正、保守といったことは経験あるけど、一からドメインイベント探してモデリングするみたいな経験は今まで正直なかったので、本当に学びしかないなと思ってやっていた。

ちなみに、新しいプロジェクトで書く言語はGoなのでこれがまた楽しみである!正直、チームで使う言語に強いこだわりはなくて保守・運用が楽になったり、いまチームが抱えている問題を解決できる言語であれば正直何でも良い。それよりも、新しい言語や手法を学ぶコストが〜と言われて選択肢を狭められる方が嫌だから。

とにかくいろいろ雑に書いたけど、新しい環境を楽しめていると思う。そして、チームメンバーも良い人が多いし、やっていき・のっていき精神を持つ人が多いからこういうことを取り入れよう!学ぼう!と言いやすいチームだなと思う。

今の環境は自分があまり触れてこなかった技術に触れる機会が多すぎるので毎日学ぶことが多い。というか学ばないとついていけないんじゃないか?という不安は常にあるけど、それを乗り越えてチームにより貢献できる人になりたいとは思っている。あと大事なことだけど、ちゃんと給料上げてもらいたいから学んでそれを仕事につなげて結果を出していきたいところ(お金はすべてを癒すのさ)

自分もいい歳なので、チームを技術面でもっと引っ張っていけるように技術を研鑽して日々を積み重ねていきたい所存です。

M1 Mac(arm64)環境でsbt-protocがエラーになる

M1 Mac(arm64)でプロジェクトの環境構築をしているとsbt-protocでエラーが出たので、調べた内容と解決方法を残しておこうと思います。

前提

  • MacBook Pro(arm64)
  • sbt v1.6.1
  • sbt-protoc v1.0.2

原因

protobuf3.17.3 以前のバージョンではM1 Mac向けのbinaryを配布していていないのが原因です。

sbt-protocv1.0.2のコードを見てみると、PB.protocVersion3.15.6 に指定されているため、M1 Macではエラーになっていました。

原因について言及されているIssue github.com

sbt-protocのv1.0.2v1.0.5の差分 github.com

protobufのM1 Mac Suuportについて言及されているIssue

github.com

protobuf3.17.2のmavenのリポジトリを見てみると、M1 Mac向けのバイナリが配布されていないのが分かります。

repo1.maven.org

解決方法

sbt-protocのバージョンをv1.0.5 以上に上げてください。

sbt-protocのv1.0.5PB.protocVersionを確認すると3.19.1に指定されているため、これ以上に上げればM1 Macでもエラーにならずに使用できると思います。

github.com

僕は現時点で最新のv1.0.6にアップデートすることでエラーを解消しました。(2022/1/16 時点)

『一瞬で判断する力 私が宇宙飛行士として磨いた7つのスキル』を読んだ

転職先の入社前面談の際に教えてもらった本で、その場でポチってすぐ読んでみました。

宇宙飛行士である若田光一さんが書かれた本です。

若田さんは日本人初のコマンダー(船長)を務めた方で、宇宙に旅立つ前の訓練での出来事や、実際に国際宇宙ステーション(ISS)でコマンダーとして仕事をされた体験などを交えながら、一瞬で判断するために必要なことを7つのキーワード(テーマ)を用いて書かれています。

ここがおもしろい!

1. 一般の会社や組織で起きそうな問題は宇宙飛行士の方たちにも起きるということ

宇宙飛行士の仕事っていうのは全く何をするか知らなかったので、漠然とすごいことをしているんだろうな、めちゃめちゃ能力が高い人たちだから自然と上手くいきそうだよな、とどこか別の次元の人たちのように個人的に思っていました。

ただ本を読み進めると、会社や組織で起こる問題は宇宙飛行士やその関係者の間にも起きるし、僕たちと同じようにどう解決するか頭を悩ませるということです。

例えば、コマンダーとして仕事をしていると、地上管制官と人たちとISSの宇宙飛行士の間で意見が食い違うことがやはりあるそうです。こういう問題は、普段の人間関係の間でも起こり得ることなので、その場合はどういう風に解決したのかというのは自分の体験を思い出しながら読むと大変面白かったです。

2. 不確定要素への対応

宇宙での作業は危険なことが多いので、事前にマニュアル・ドキュメントをしっかり書いて読み込むのだそうです。

また、想定外にも対応できるようにできるだけ不確定要素を減らしたり、もし複合的なトラブルが発生しても解決できるように「たぶん」や「〜ではないだろうか」で判断せず、予知できるレベルにまで体制を整えるそうです。

このような仕事のやり方はしっかり見習いたいのと同時に、エンジニアがサービスをリリースするときにどこか似ているなと感じました。

アプリケーションの設定は大丈夫か?、メモリの容量は適切か?、リクエストがこれぐらい来ると予測しているが、スパイクにも耐えられるような体制にもしておくか等、不確定要素に対しての対応を考えるというのは参考になるなと読んでいて思いました。

3. 学びについて

第2章の「学ぶ」の内容がものすごく良かったので少しだけ記載します。 宇宙飛行士の方たちは、わからないこと・曖昧なことがをそのままにすると死に直結するため、ここまで徹底的にやられているのだと思います。

新しい環境に飛び込んだときは質問しづらい気持ちになったりすると思います。また、歳を取ると分からないことに対して今更質問するのが恥ずかしいとか、まぁいいやという気持ちになってくるのかもしれません。

そんな気持ちになったときは、この本の内容を思い出して積極的に発言・質問していきたいなと思いました。

  • 愚かな質問はない
    • 「理解したつもり」が危ない
  • 質問は、自らの理解を深めるためでもある
  • 自分が感じた疑問点をそのまま残しておいてはいけない
  • 何よりも「質問をする」という行為自体が、自分の頭で考え、「理解できている部分」と「そうでない部分」を明確に分別することで生まれ出るのも事実である
  • 「分からないまま」に甘んじない
  • あやふやな記憶は、トラブルのもととなる
  • 急いで仕事をススメなければならないときに忘れてはならないのは、「何のためにやっているか」を思い出すこと
  • 長い目で見ると、速く理解することよりも重要になるのは、長い時間をかけてでも、キチンと正しく理解すること
    • 本当の意味でその知識を咀嚼していなければ意味がない
    • 中途半端な身につけ方をしていれば、結局またいつか学び直さなければならなくなる
  • 失敗を隠さず、失敗に対して常に鋭敏にアンテナを張り、失敗から何かを学ぶ姿勢が重要
  • 同じ失敗を繰り返さない
    • 一回目の失敗は、ときには寛容に受け入れてもらっても、二回目に同じ失敗をすると信頼は失墜する
    • 自分が犯した失敗を真摯に受け止め、その理由をきちんと分析し、とるべき最善の対応策を検討して習得することを怠らなければ、今後、類似した状況で同じような失敗を繰り返す可能性は少なくなる

本の内容について

最初、タイトル通り「一瞬で判断する力」について書かれていたのかと思っていましたが、ざっくり以下のような感じです。

  • 仕事への向き合う姿勢
  • 学び方
  • ものごとを前に進める方法
  • チームワーク
  • 信頼関係の構築方法
  • 決断する方法
  • 恐怖への向き合い方
  • リーダーシップ

勘違いしてほしくないのが、方法論が書かれているわけではなく、若田さんの実体験とこう行動したのが良かったというのが書かれています。

なので、ここから自分ならどうするのか、自分なりに咀嚼する必要がある本だと感じています。

どのような人におすすめか?

新しい環境に飛び込む方、マネジメントをされている方、人間関係で悩まれている方などでしょうか。

最後に

久しぶりにおもしろくて一気読みしてしまいました。

このブログでおもしろさを伝えきれないと思うので、ぜひ手にとって読んでみてください!

AWS Certified Cloud Practitionerに合格した

2021年9月26日(日)にAWS Certified Cloud Practitioner(CLF)に合格しました!

2021年9月24日時点でCLFを残し、その他は合格していたので思い切ってほぼ勉強期間を設けずに試験に臨みました。

といってもさすがに不安だったので、前日にAWSが提供するCLFの対策デジタルトレーニングやサンプル問題を解いて、当日は本屋さんでCLFの対策本を立ち読みしました。

問題自体が普段受けていたAssociateやProfessional、Specialtyに比べて具体性がなくてとっつきにくい印象を持ちましたがなんとか合格することができました!

www.credly.com

2021年の個人目標として「AWS11冠」を掲げて頑張ってきたので、とにかく年内に定めた目標を達成できてよかったなと思います。

11冠を取得して振り返りたいこともあるので、これは別のブログ内容として書こうかなと思います。

以上!

AWS Certified Advanced Networking - Specialtyに合格した

本日、2021年9月24日(金)にAWS Certified Advanced Networking - Specialty(ANS)に合格しました🎉🎉

スコアとセクションごとのパフォーマンス

今回受けたANSのスコアは 752 で合格ラインギリギリでした!

f:id:ryskit:20210924234413p:plain

そして、セクションごとのパフォーマンスは画像のとおりです。

さすがにギリギリ合格なので、改善が必要な部分が多いです。

f:id:ryskit:20210924234749p:plain

実際に試験を受けてみた感想

ANS関連のサービスのドキュメントやDirect Connectのブログ記事など読み漁り模擬試験も3周ほどしてほぼ満点まで取れるようにしたのですが、ちゃんと理解できていなかったようで本番試験の問題は上手く解けませんでした。

やはり、Direct ConnectやVIF、BGP、Site-to-Site VPNなど学んだことをうまく整理できてなかった感が否めません。

もともとインフラエンジニアではないので出てくる用語やサービスについては具体的なイメージを想像しづらく、ドキュメントに書いていることを暗記するだけになってたのが良くなかった気がします。

今後どうするか?

正直、Direct Connect等は構築することはほぼないと思うので興味が出ないのですが、それ以外のネットワークのサービスだったり知識に関しては勉強したいなと思うので引き続き継続して書籍やドキュメントを読んで理解を深めたいと思います。

最後に

AWS ANSでやっと10冠取得できました。

あとは、一番簡単なクラウドプラクティショナーを取得できれば今あるAWS資格をコンプリートできるので引き続き頑張ろうと思います!

手動でダウンロードしたsbtをsbtenvで管理する

もともとはhomebrewでsbtをインストールして使っていましたが、sbtの複数のバージョンを使って色々検証したくなりsbtenvを使ってみることにしました。

sbtenvでsbtをインストールしようとすると僕の環境ではgpg周りでsignatureの検証が失敗してしまい、すぐに解決できそうになかったので、手動でsbtをダウンロードしてsbtenvで管理する方法を取りました。

手順

前提として、xxxenv系のツールはanyenvで管理しているため、直接sbtenvをインストールして使う場合と少しパス等が違う可能性があるのでご注意ください。

1. anyenvでsbtenvをインストールする

$ anyenv install sbtenv

以下のコマンドを実行して、sbtenvのバージョンが返ってこればOK!

$  sbtenv -v

sbtenv 0.0.24

2. sbtの公式ページからsbtをダウンロードする

公式ページから sbt 1.5.5.tgz をダウンロードします。

www.scala-sbt.org

f:id:ryskit:20210923165209p:plain

3. sbtenvのversionsにディレクトリを作成する

~/.anyenv/envs/sbtenv/versions 配下にsbtのバージョン番号と同じディレクトリを作成します。

$ mkdir -p  ~/.anyenv/envs/sbtenv/versions/1.5.5

4. ダウンロードしたsbtをファイルを解凍する

tar xf ~/Downloads/sbt-1.5.5.tgz -C ~/.anyenv/envs/sbtenv/versions/1.5.5/

5. sbtenvで管理されているか確認する

以下のように、sbtenv versions を実行して、1.5.5の横に*が付いていたらOKです。

$ sbtenv versions

  1.2.8
  1.3.9
* 1.5.5 (set by /Users/[username]/.anyenv/envs/sbtenv/version)

付いていなければ、以下を実行してください。

$ sbtenv global 1.5.5

6. sbtの情報を表示してみる

welcome to sbt 1.5.5 と表示されていればOKです。

$ cd ~ && sbt about

warning: ignoring extraneous `sbt-` prefix in version `sbt-1.5.5`
         (set by /Users/[username]/project/build.properties)
[info] welcome to sbt 1.5.5 (Amazon.com Inc. Java 11.0.3)
[info] loading global plugins from /Users/[username]/.sbt/1.0/plugins
[info] loading project definition from /Users/[username]/project
[info] set current project to ryskit (in build file:/Users/[username]/)
[info] This is sbt 1.5.5
[info] The current project is ProjectRef(uri("file:/Users/[username]/"), "username") 0.1.0-SNAPSHOT
[info] The current project is built against Scala 2.12.14
[info] Available Plugins
[info]  - sbt.ScriptedPlugin
[info]  - sbt.plugins.CorePlugin
[info]  - sbt.plugins.Giter8TemplatePlugin
[info]  - sbt.plugins.IvyPlugin
[info]  - sbt.plugins.JUnitXmlReportPlugin
[info]  - sbt.plugins.JvmPlugin
[info]  - sbt.plugins.MiniDependencyTreePlugin
[info]  - sbt.plugins.SbtPlugin
[info]  - sbt.plugins.SemanticdbPlugin
[info] sbt, sbt plugins, and build definitions are using Scala 2.12.14

参照

github.com

www.scala-sbt.org