Scalaのcase classの第二パラメータリストに外からアクセスできなくて困った件

ケースクラスの2つ目の引数が外から見えない、なぜだろう?ということが発端となり、いろいろと調べたりGitterで質問してみました。その結果、コンストラクタ引数の定義のしかたによって見え方(アクセス修飾子の状態)が異なるということがわかりました。


scala> case class A(x: Int)(y: Int){

     | def printY() = println(y)

     | }

defined class A

scala> val a = A(10)(20)

a: A = A(10)

scala> a.x

res22: Int = 10

scala> a.y

<console>:19: error: value y is not a member of A

              a.y

                ^

scala> a.printY()

20

クラスののデフォルトコンストラクタ引数はprivateです。したがって、下の例ではprivateなフィールドxにアクセスしようとしてエラーが出ています。


scala> class A(x: Int)

defined class A

scala> new A(10)

res0: A = A@af23093

scala> res0.x

<console>:13: error: value x is not a member of A

       res0.x

            ^

scala> 

私は普段case classをよく使う一方、クラスのフィールドに外からアクセスすることはあまりないので、すっかり頭のなかから抜け落ちていました。

このフィールドをpublicなものとするには、 val または var を明示的に付けてあげる必要があります。


scala> class A(val x: Int)

defined class A

scala> new A(10)

res0: A = A@6deaf732

scala> res0.x

res1: Int = 10

あるいは、case classとすると、valやvarを付加しなくても自動生成されたゲッターメソッドによってフィールドにアクセスできます。


scala> case class A(x: Int)

defined class A

scala> A(10)

res0: A = A(10)

scala> res0.x

res1: Int = 10

しかしながら、case classは万能ではありません。case classに複数のパラメータリストが存在する場合、2つ目以降のパラメータリストにはゲッターメソッドを生成してくれません。


scala>  case class B(x: Int)(y: Int)(z: Int)

defined class B

scala> B(10)(20)(30)

res0: B = B(10)

scala> res0.x

res1: Int = 10

scala> res0.y

<console>:14: error: value y is not a member of B

       res0.y

            ^

scala> res0.z

<console>:14: error: value z is not a member of B

       res0.z

            ^

それどころか、case classはequals()やhashCode()についても2つ目以降のパラメータリストを無視してくれるので、以下のように「第一パラメータだけで比較する」「ハッシュコードが同一になる」といった現象が起こります。
https://gitter.im/scalajp/public


scala> case class C(x: Int)(y: Int)

defined class C

scala> C(10)(20)

res0: C = C(10)

scala> C(10)(30)

res1: C = C(10)

scala> res0 == res1

res2: Boolean = true

scala> res0.hashCode

res3: Int = -2008924253

scala> res1.hashCode

res4: Int = -2008924253

結論:気をつけます。

参考


http://www.ne.jp/asahi/hishidama/home/tech/scala/class.html#h_construntor


http://www.ne.jp/asahi/hishidama/home/tech/scala/class.html#h_case_class

Scalaをちょろっと書く方法

ちゃんとしたScalaプロジェクトを書くなら、相当に腕の立つ方でない限りIDEを使って書いたほうがいいですが、簡単なプログラムを動かしたいという時には小回りがきかずやや面倒です。

ここでは、テキストのサンプルコードをちょっと動かしてみたいときに使える方法を挙げます。

REPLで動かす

  • scalaにパスが通っている場合: scala と入力して実行

  • activatorにパスが通っている場合: activator と打ってから console と入力、または activator console と入力して実行

  • sbtにパスが通っている場合: sbt と打ってから console と入力、または sbt console と入力して実行

このあと好きなように入力します。

REPLでファイルを指定して実行する

script.scala という名前で以下のような内容のファイルを作成する。


object HelloWorld extends App {

  println("Hello, world!")

}

HelloWorld.main(args)

そしてREPLを起動して、 script.scala と入力して実行する。

シェルスクリプトとして動かす

scalaがインストールされているのを前提。

script.sh という名前で以下のような内容のファイルを作成する。


#!/bin/sh

exec scala "$0" "$@"

!#

object HelloWorld extends App {

  println("Hello, world!")

}

HelloWorld.main(args)

そして chmod +x script.sh と実行権限を付加した後、 ./script.sh と入力して実行。

paiza.ioで動かす

paiza.ioならばWebブラウザ上でScalaのコードを動かすことができる。

以上です。

環境

Macのターミナル

Someにタプルを渡したら「Adapting argument list by creating a 4-tuple: this may not be what you want」という警告を受けた

Optionにタプルを包もうと考え、以下の例のように書きました。


Some(userId, None, Some(itemId), item)

すると -Xlint オプションのお陰で、以下のように警告されました。


Adapting argument list by creating a 4-tuple: this may not be what you want.

[warn]         signature: Some.apply[A](x: A): Some[A]

[warn]   given arguments: userId, None, Some(itemId), item

[warn]  after adaptation: Some((userId, None, Some(itemId), item))

[warn]           Some(userId, None, Some(itemId), item)

「はて、うまく渡っているようだが、どういうことだろう」と考え、その前で @unchecked アノテーションを付けるなどしてみたがうまくいかず。

この警告の意味は要するに、

「Someの引数はひとつなのに、与えられた引数は4つもある。仕方がないからタプルに変換して渡してやったが、これはアンタの考えている挙動と異なるかもしれないから、よく確認しろ!」

ということです。

以下のように、タプルを明示的に渡してあげると、警告はなくなりました。ちゃんとタプルとして渡せているのかどうか、注意しなければなりませんね。


Some((orderDetail.sellerId, None, Some(orderDetailComment.orderDetailId), orderDetail))

安易に警告を抑制(@suppressWarnings)できなくてよかったです。

参考

引数の数間違えたと思ったらタプルになってた

iPhoneアプリの開発者名を変更する

要旨

アプリ開発者名が、テストで作成したアプリで設定したダミー名(devtest)となっていたので、これを変更するために電話した。

必要なもの

デベロッパーサポートへの電話番号

https://developer.apple.com/contact/phone.php

推移

電話してiPhoneアプリ開発者名を変更したい旨を述べる。

アカウント確認(氏名とメールアドレス)のあと、該当箇所についてApp Storeを見ながら確認。

二箇所ある開発者名欄のうち、正しい方(iTunesConnectで設定できる方)に合わせる。あらかじめiTunesConnectを確認しておくと吉。

24時間以内に反映されるとのこと。

9桁のケース番号を貰って終了。最後までスムーズだった。

備考

もし反映されなかった場合は、再度問い合わせてケース番号を伝えれば良い。

iTunesConnectで設定できる開発者名と揃えたくない場合、どうなるかは疑問。

参考

http://matsutsune.hatenablog.com/entry/2013/08/13/142837

http://takasa.blogspot.jp/2012/11/itunes-app-store_29.html

http://www.qeq.jp/it/iphone/2013/03/itunes.html

MySQL says "Cannot add foreign key constraint" when execute sql generated with MySQL Workbench

MySQL Workbenchで生成したSQLがなぜか実行時に"Cannot add foreign key constraint"と言われてしまい失敗するという話です。

ER図上で、外部キー制約を付けた二つのカラムの型が異なっていたために生じていました。例えば、 INTVARCHAR(45) といった形です。

修正したら首尾よく実行されました。

参考:MySQL Workbench Forward Engineer Error 1215: Cannot add foreign key constraint