Skip to content

1フリーエンジニアが働き方について考えた

1フリーエンジニアが働き方について考えた published on 1フリーエンジニアが働き方について考えた へのコメントはまだありません

こんな記事があった

久しぶりに技術系以外の事を書く気が。

こんな記事があった↓

<ゆるい就職>若者が正社員で働くのは「負け」 慶大助教が提案

Yahoo!の記事は結構すぐ消えてしまうので、いくつか引用しておく。

慶応大特任助教、若新雄純(わかしんゆうじゅん)さんが提案する「ゆるい就職」が話題だ。若者に「週休4日で15万円」の仕事を紹介する人材派遣サービスという。

この人、元記事では書いてないけど、NEET株式会社の人らしい。

「今どき、若い世代が正社員で働くのって『負け』だと思うんです」。正社員で長時間労働に苦しむシステムエンジニアの男性(23)が発言すると、会場に集まった約60人の若者から賛意のどよめきが起こった。

という、参加者の声。

つまり週3日働いて月収15万円の仕事を紹介するという。派遣など非正規雇用。15万円は額面。手取りでは12万~13万円か。週3日勤務は社会保険の適用外だ。

というシステムらしい。

Continue reading 1フリーエンジニアが働き方について考えた

Scalaの型引数とかサブ型とか

Scalaの型引数とかサブ型とか published on Scalaの型引数とかサブ型とか へのコメントはまだありません

コンパイルが通らない

以下のコードのコンパイルが通らないので、どうしたものかと質問した。

abstract class C
case class C1() extends C
case class C2() extends C

trait Service[T <: C] {
  def getFoo(): Option[T]
}

object Service {
  // found   : List[Option[Any]]
  // required: List[Option[T]]
  def getAllFoos(services: List[Service[_]]): List[Option[C]] = {
    services.map(_.getFoo())
  }
}

(答えだけ知りたい場合は、@gakuzzzz さんのコメントが簡潔かつ完全な内容なのでそちらを参照。)

エラーの原因

さて、エラーの原因は @gakuzzzz さんの解説にある通り。

ポイントは trait Service[T <: C] にした場合、 Service[C] と Service[C1] と Service[C2] が全然関係ない型になってしまうので、統一的に扱えないって所ですね。

(なぜ全然関係ない型になってしまうのだろう。調べないと。)

試行錯誤

@xuwei-k さんに教えてもらった方法

@xuwei-k さんに教えてもらった方法だとコンパイルは通った。

object Service {
  def getAllFoos[A <: C](services: List[Service[A]]): List[Option[C]] = {
    services.map(_.getFoo())
  }
}

ただ、元のgistで端折ってしまったんだけど、@gakuzzzz さんが書いたように、実際にはServiceは以下のように継承して使うことを前提としてる。

object C1Service extends Service[C1] { def getFoo(): Option[C1] = None }
object C2Service extends Service[C2] { def getFoo(): Option[C2] = None }

servicesにC1ServiceとC2Serviceが混ざったものを渡すとエラーになった。

scala> Service.getAllFoos(List(C1Service))
res4: List[Option[C]] = List(None)

scala> Service.getAllFoos(List(C2Service))
res5: List[Option[C]] = List(None)

scala> Service.getAllFoos(List(C1Service, C2Service))
<console>:17: error: no type parameters for method getAllFoos: (services: List[Service[A]])List[Option[C]] exist so that it can be applied to arguments (List[Service[_ >: C2 with C1 <: Product with Serializable with C]])
 --- because ---
argument expression's type is not compatible with formal parameter type;
 found   : List[Service[_ >: C2 with C1 <: Product with Serializable with C]]
 required: List[Service[?A]]
              Service.getAllFoos(List(C1Service, C2Service))
                      ^
<console>:17: error: type mismatch;
 found   : List[Service[_ >: C2 with C1 <: Product with Serializable with C]]
 required: List[Service[A]]
              Service.getAllFoos(List(C1Service, C2Service))

エラーの意味がよく分からないが、「Service[C] と Service[C1] と Service[C2] が全然関係ない型になってしま」ってるという事なのかな。

@gakuzzzzさんに書いてもらった方法1

こちら

abstract class C
case class C1() extends C
case class C2() extends C

trait Service[+T <: C] { // ここがポイント
  def getFoo(): Option[T]
}

object Service {
  def getAllFoos(services: List[Service[C]]): List[Option[C]] = {
    services.map(_.getFoo())
  }
}

object C1Service extends Service[C1] { def getFoo(): Option[C1] = None }
object C2Service extends Service[C2] { def getFoo(): Option[C2] = None }

Service.getAllFoos(List(C1Service, C2Service))

うまく行った。

これを trait Service[+T <: C] とすれば、Service[C1]Service[C2]Service[C] のサブ型と見做せるようになるので Service[C] として統一的に扱えるようになります。

とのこと。

@gakuzzzzさんに書いてもらった方法2

gistはこちら

abstract class C
case class C1() extends C
case class C2() extends C

trait Service {
  type T <: C
  def getFoo(): Option[T]
}

object Service {
  def getAllFoos(services: List[Service]): List[Option[C]] = {
    services.map(_.getFoo())
  }
}

object C1Service extends Service { type T = C1; def getFoo(): Option[C1] = None }
object C2Service extends Service { type T = C2; def getFoo(): Option[C2] = None }

Service.getAllFoos(List(C1Service, C2Service))

こちらに関しても先生のコメント。

型引数を止めて、抽象メンバ型でやれば、Service という型は一つになるので、こちらも Service で統一的に扱えるようになります。 type T = C1 が必要なのはこの場合ですね。

使い分け

これも先生のコメントそのまま書いとく。

共変を使うか、抽象メンバ型を使うか、の判断基準としては、「C1に特化したServiceの型を扱いたいかどうか」になるかと思います。

なるほど。

考えたこと

これだけだと、@xuwei-k さんと @gakuzzzz さんのコメントを切り貼りしただけなので、もう少し書いておく。

そもそもの動機

実際のコードだと、C1とかC2はユーザーの外部アカウントとの連携情報で、例えば以下の様なもの(簡略化してるけど)。どれとも連携していない人もいれば、全てと連携している人もいる。

  • Facebook連携の情報: UserFacebookInfo
  • Twitter連携の情報: UserTwitterInfo
  • GitHub連携の情報: UserGitHubInfo

それぞれが抽象クラスUserExternalAccountInfoを継承している。

で、Serviceに当たるのはいくつかあるけど、例えばDAOで、それぞれ違うテーブルにデータが入っているので(それぞれ user_facebook_info, user_twitter_info, user_github_info テーブル)、別々のDAOを用意した。以下の感じ。

trait ExternalAccountInfoDao[T <: UserExternalAccountInfo] {
  def getInfo(userId: Int): Option[T]
}

object ExternalAccountInfoDao {
  def getAllAccounts(userId: Int): List[Option[UserExternalAccountInfo]] = {
    val daos = List(UserFacebookInfoDao, UserTwitterInfoDao, UserGithubInfoDao)
    daos.map(_.getInfo(userId))
  }
}

object UserFacebookInfoDao
 extends ExternalAccountInfoDao[UserFacebookInfo] {
  def getInfo(userId: Int): Option[UserFacebookInfo] = { /* code */ }
}
// UserTwitterInfoDao, UserGithubInfoDao も同様

(多分もっと良い設計がありそうなので、コメント大歓迎です。)

その他

コメントで書いてもらったコードを見れば内容は分かるんだけど、自分であういうコードがパッと出てこないところが実力不足だと思った。

人のコードを沢山読んだり、定石(いわゆるデザインパターン)をもっと勉強しないといけないなと思った。

sbtでstacktraceを全部表示させる

sbtでstacktraceを全部表示させる published on sbtでstacktraceを全部表示させる への2件のコメント

自分用メモ。

Play! frameworkを使った開発で、エラー発生時にstacktraceが途中で途切れてしまう事がある、

ScalaTestの場合は、こちらにあるように以下のように指定する。

testOptions in Test += Tests.Argument("-oD")

sbt自身の話だが、デフォルトでは大抵のstack traceは隠されてしまう。

By default, sbt hides the stack trace of most exceptions thrown during execution. It prints a message that indicates how to display the exception. However, you may want to show more of stack traces by default.

表示するためには以下のようにする。詳しくはマニュアル参照。

> set every traceLevel := 0

最初からonにしててくれればいいのに。

追記:

xuwei_kさんからのコメントの通り、lastとやると、最後に実行したコマンドに関するログが出力される。マニュアルの一番上に書いてある。

When a command is run, more detailed logging output is sent to a file than to the screen (by default). This output can be recalled for the command just executed by running last.

新人を教えて学んだこと

新人を教えて学んだこと published on 新人を教えて学んだこと へのコメントはまだありません

経験1年未満の新人を教えることになった。

ここ数年は、たまにフルタイムになったりとかしたけど、基本的にはフリーで仕事をしてるので、自分1人もしくは割と経験のある人とチームを組んで仕事をすることが多かった。

ただ、最近仕事が多くなってきたので、今後も見据えて、経験の浅いエンジニアを1人雇う事にした。今までもプロジェクトリーダーとかでメンバーを指導という経験はあったんだけど、プログラムを始めて1年未満の新人を教えるのは今回が初めてなので、教える側の自分にとっても色々と勉強になっている。

Continue reading 新人を教えて学んだこと