hogepiyoエンジニアリング

トラブルシューティングからノウハウ、作ってみた系まで扱う情報系ブログ

Scalaで学ぶ共変と反変

最近コップ本を読みながらScalaをちょろちょろ勉強しているんですが、共変と反変のところで頭がこんがらがってきたので頭の整理の意味もこめて記事にします。

こちらがまとまっていて非常に助かりました。qiita.com
基本的にこちらの記事を参照すればいいと思うけど、反変は戻り値に使えないとか共変は引数に使えないという部分が理解するのに時間かかったので具体的なソースコードを交えてまとめます。

まず具体的なソースコードはこちら。
コンパイルエラーが出ます。

class Creature

class Animal extends Creature

class Cat extends Animal

// +Tが共変、-Uが反変の意味
class Container[+T, -U] {

  //引数でエラー
  def f(arg: T): T = {
    new T()
  }

  //戻り値でエラー
  def f2(arg: U): T = {
    new T()
  }
  
  //戻り値でエラー  
  def f3(): U = {
    new U()
  }

}

さて、なんでこれでコンパイルエラーになるのかっていう話ですね。

共変を引数にできない話

f関数から見て行きましょう。
これは引数でエラーが出ています。

変数cの左辺の型はContainer[Animal, Animal]です。
Tは共変であるから、左辺のAnimal型に対して右辺をAnimalかCatで指定できます。
しかしCat型で定義した場合に、Containerクラスのf関数の引数がCat型を受け入れるのに対し、変数cがContainer[Animal, Animal]型であるためf関数にAnimal型を渡せてしまいます。
ここで、Cat型にAnimal型は入れられないので矛盾が発生します。
そのため共変は引数に使えません。

def main(args: Array[String]): Unit = {
  val c: Container[Animal, Animal] = new Container[Cat, Animal]()
  c.f(new Animal()) //Cat型の定義にAnimal型を渡せてしまう
}

//共変の型にCatを指定した場合のContainer定義
class Container[Cat, Animal] {
  //f2, f3関数は省略

  def f(arg: Cat): Cat = { //引数の型がCat型
    new Cat()
  }
}
共変を戻り値にできる話

f2関数を見ます。
これは引数も戻り値もエラーは出ていません。

変数cの左辺の型はContainer[Animal, Animal]であるから、戻り値の型はAnimal型です。
共変の場合、右辺として許される型はAnimalかCatなので戻り値として許される型もAnimal型かCat型になります。
これは左辺と矛盾しません。
よって共変は戻り値として使えます。

def main(args: Array[String]): Unit = {
  val c: Container[Animal, Animal] = new Container[Cat, Animal]()
  val a: Animal = c.f(new Animal()) //共変のため戻り値としてはAnimalかCatしかありえないのでAnimal型変数aで受け取れる。
}

//TにCatを指定した場合のContainer定義
class Container[Cat, Animal] {
  //f, f3関数は省略

  def f2(arg: Animal): Cat = {
    new Cat()
  }
}
反変を戻り値にできない話

f3関数を見てみましょう。
これは戻り値でエラーが出てます。

Uは反変ですから、左辺のAnimal型に対して右辺をAnimalかCreatureで指定できます。
変数cの左辺はAnimal型が指定されているので「戻り値としてAnimal型が返ってきますよ」と定義されています。
しかし、右辺としてCreature型を指定した場合、戻り値がCreature型になってしまうのでこれは左辺と矛盾します。
よって反変は戻り値として使えません。

def main(args: Array[String]): Unit = {
  val c: Container[Animal, Animal] = new Container[Animal, Creature]()
  val a: Animal = c.f3()
}

//UにCreatureを指定した場合のContainer定義
class Container[Animal, Creature] {
  //f, f2関数は省略

  def f3(): Creature = {
    new Creature()
  }
}
反変を引数にできる話

f2関数を見ます

左辺の型がAnimalなので、引数として渡される可能性があるのはAnimal型かCat型です。
反変なので、右辺のジェネリック型としてCreature型かAnimal型を指定できます。
つまり「Creature型かAnimalを引数にとるように定義されている関数に対して、Animal型かCat型を渡して実行することができる」ということなので、これは矛盾しません。
よって反変の型は引数に指定できます。

def main(args: Array[String]): Unit = {
  val c: Container[Animal, Animal] = new Container[Animal, Creature]()
 c.f2(new Animal())
  c.f2(new Cat())
}

//UにCreatureを指定した場合のContainer定義
class Container[Animal, Creature] {
  //f関数は省略

  def f2(arg: Creature): Creature = {
    new Creature()
  }
}

Intellij14でGoのSDKが設定できない件

IntellijでGoのプロジェクトを扱うためにはGoのプラグインを入れる必要がある

pluginsの設定からgolangで探すと確かにプラグインがある。よし入れよう。

が、Intellij14.0.2だとSDKの設定画面でGoのホームディレクトリを設定しよとしてOKボタンを押しても反応しない・・なんだよこれ

f:id:marshi:20150131175607p:plain


どうやら14.0.2ではこのプラグインを入れても動かないらしい。go-lang-plugin fails to set SDK for go 1.4 and intellj 14.0.2 · Issue #1132 · go-lang-plugin-org/go-lang-idea-plugin · GitHub

ということで一旦goのプラグインをアンインストールして、下記の0.9.16-alpha.9 google-go-language.jarを落としてきてpluginsの設定からInstall plugin from diskでこのjarを指定してプラグインをインストールすればOKReleases · go-lang-plugin-org/go-lang-idea-plugin · GitHub

動いたー\(^o^)/

MySQL Fabricを動かしてみた

ぶっちゃけほとんどこちらのブログ参考
日々の覚書: MySQL Fabricつらい(インストール編)
日々の覚書: MySQL Fabricつらい(HA/登録編)

MySQLの設定

/etc/my.cnf

//マスタースレーブ両方で設定
[mysqld]
#replication
log-bin = mysql-bin
log-slave-updates = true
gtid-mode = on
enforce-gtid-consistency = true
server-id=1000 // 各ノードで別の値にすること。

勘違いしてたこと

fabricのコマンドを打てばmysqlのmaster-slave構成の設定してくれるんでしょ?

$ mysqlfabric group promote my_first_fabric //fabricで管理しているノードからマスターを選出するコマンド

これだけだとダメだった。
mysqlreplicateなりでmaster-slaveの設定をしてあげないといけなかった。ここで行ったmaster-slave構成に合わせてfabricを設定するって感じかと。

$ mysqlreplicate --master=fabric:fabric_password@"10.10.10.10" --slave=fabric:fabric_password@"11.11.11.11" --rpl-user=fabric:fabric_password

一回設定してしまえばあとはマスターが死んでもfailoverした状態で自動的にマスターとスレーブを認識してくれる。(ちゃんと障害検知設定していれば)

ノードが死んだときの障害検知ってfabricでgroupに追加しておけばやってくれるんでしょ?

$ mysqlfabric group add fabricgrp 10.10.10.10 //fabricの管理しているノード郡に追加するコマンド

これだけだとダメだった。マスターが死んでもそのまま放置される。
ちゃんと障害検知を有効化すれば、マスターが死んだ時にスレーブをマスター昇格してくれる。

$ mysqlfabric group activate fabricgrp

Intellijで行番号が表示されなくなったときの対処

IdeaVimの0.38からの模様

下記の2行目
f:id:marshi:20141208121705p:plain

これが起こると「行番号を表示」にチェックしてもどこかをクリックすると行番号が非表示にされてしまう。

対処法

ユーザフォルダに_ideavimrcという名前でファイルを作って下記のように設定。そしてIntellijを再起動。

set number

Hubotで実行したスクリプトの回数をカウントするスクリプト

qiitaに投稿した。
http://qiita.com/marshi@github/items/f8c370774d504158895c

ソースコード


marshi/hubot_script_counter · GitHub

実装

hubot-scriptのエイリアス - Qiita を参考にしてreceiveメソッドをフックして、いずれかのスクリプトが実行されるたびに実行回数のカウントアップを行う。
カウントにはrobot.brainの下記メソッドを使っている。

robot.brain.get
robot.brain.remove
robot.brain.set

これらのメソッドはrobot.brain.data["_private"]とのデータのやり取りを行う。
カウントの増加を行うために、robot.brain.data["_private"]に保存されているカウントデータ("script_#{script_name}"がkey、実行回数がvalue)の一覧から実行されたスクリプトに該当するデータを検索し、カウントを1上げている。この際、JSON.parseを使うとsyntax errorが出るためevalを使っている。
同様に、カウントデータ一覧から指定されたスクリプトを検索して、削除・任意のカウントへの更新を行う。

IntelliJ上でtomcat起動時にClassNotFoundExceptionがでるときの対処(Maven管理プロジェクト)

Eclipdeだと大概Assembly Deploymentでmavenを追加すれば直るわけですが、IntelliJだとそれに類する機能が見当たらないのでどうしましょっていう話

ClassNotFoundExceptionが出る原因

ビルドは通るくせにtomcat起動時にExceptionが出るもんだから「は?」となるがtomcatが参照するディレクトリにファイルが生成されていないことが原因

対処法

.javaファイルは $APP_HOME/WEB-INF/classes、ライブラリは $APP_HOME/WEB-INF/lib配下に置くようにする
置く場所を指定するにはpom.xmlに下記のように記述する
ライブラリに関しては結局コピーするくらいしか方法が見つからなかった
実は設定できるんだったら情報ほしいです。。

<project>

<!-- なんかその他設定 -->

<build>
  <plugins>
    <plugin>
      <artifactId>maven-compiler-plugin</artifactId>
	<configuration>
	<encoding>UTF-8</encoding>
	<source>1.8</source>
	<target>1.8</target>
	</configuration>
      </plugin>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-dependency-plugin</artifactId>
      <executions>
      <!-- copy-dependenciesで依存関係のあるライブラリをlibディレクトリにコピー -->  
      <execution>
        <id>copy</id>
	<phase>install</phase>
        <goals>
	  <goal>copy-dependencies</goal>
        </goals>
        <configuration>
	  <outputDirectory>web/WEB-INF/lib</outputDirectory>
        </configuration>
        </execution>
      </executions>
    </plugin>
  </plugins>

  <!-- .classファイルの出力先  -->
  <outputDirectory>web/WEB-INF/classes</outputDirectory>
</build>

</project>