Create a gist now

Instantly share code, notes, and snippets.

@gakuzzzz /file.md
Last active Jun 8, 2017

Embed
What would you like to do?
UserId など値型はどうするべきか

UserId などの型はどうするべきか

1. primitive 型をそのまま使う

case class Person(id: Long, name: String, organizationId: Long)
object Person {

  def groupByOrg: Map[Long, Seq[Person]] = ...

}

メリット

  1. wrap/unwrapが存在しない
  2. Boxing/Unboxingが発生しない
  3. 大抵のDBライブラリやJSONライブラリなどでそのまま扱える
  4. 定義が楽

デメリット

  1. 型安全でない
  2. シグネチャだけで意味が取れない

2. type alias を使う

package object models {

  type PersonId = Long
  type PersonName = String
  type OrganizationId = Long

}

case class Person(id: PersonId, name: PersonName, organizationId: OrganizationId)
object Person {

  def groupByOrg: Map[OrganizationId, Seq[Person]] = ...

}

メリット

  1. wrap/unwrapが存在しない
  2. Boxing/Unboxingが発生しない
  3. 大抵のDBライブラリやJSONライブラリなどでそのまま扱える
  4. シグネチャだけで意味がわかりやすい

デメリット

  1. 型安全でない
  2. 定義が手間

3. 専用の値クラスを作る

case class PersonId(value: Long) extends AnyVal
case class PersonName(value: String) extends AnyVal
case class OrganizationId(value: Long) extends AnyVal

case class Person(id: PersonId, name: PersonName, organizationId: OrganizationId)
object Person {

  def groupByOrg: Map[OrganizationId, Seq[Person]] = ...

}

メリット

  1. 型安全
  2. シグネチャだけで意味がわかりやすい
  3. 特定のケースを除いてwrap/unwrap時にオーバーヘッドが存在しない
  4. Boxing/Unboxingが発生しない
  5. メソッドが定義しやすい

デメリット

  1. 定義が手間
  2. DBライブラリやJSONライブラリなどで変換ロジックを書く必要がある
  3. 特定のケースでオーバーヘッドが存在する

4. TaggedType を使う

import scalaz._
import scalaz.Tag._

package object models {

  type Id = Long
  type Name = String

}

case class Person(id: Id @@ Person, name: Name @@ Person, organizationId: Id @@ Organization)
object Person {

  def groupByOrg: Map[Id @@ Organization, Seq[Person]] = ...

  val tagOf: TagOf[Person] = Tag.of[Person]

}

メリット

  1. 型安全
  2. シグネチャだけで意味がわかりやすい
  3. wrap/unwrap時にオーバーヘッドが存在しない
  4. 定義が楽

デメリット

  1. DBライブラリやJSONライブラリなどで変換ロジックを書く必要がある
  2. Boxing/Unboxingが発生する

まとめ

メリット デメリット
primitive 型 * wrap/unwrapが存在しない
* Boxing/Unboxingが発生しない
* 大抵のDBライブラリやJSONライブラリなどでそのまま扱える
* 定義が楽

* 型安全でない
* シグネチャだけで意味が取れない
type alias * wrap/unwrapが存在しない
* Boxing/Unboxingが発生しない
* 大抵のDBライブラリやJSONライブラリなどでそのまま扱える
* シグネチャだけで意味がわかりやすい
* 型安全でない
* 定義が手間
値クラス * 型安全
* シグネチャだけで意味がわかりやすい
* 特定のケースを除いてwrap/unwrap時にオーバーヘッドが存在しない
* Boxing/Unboxingが発生しない
* メソッドが定義しやすい
* 定義が手間
* DBライブラリやJSONライブラリなどで変換ロジックを書く必要がある
* 特定のケースでオーバーヘッドが存在する
TaggedType * 型安全
* シグネチャだけで意味がわかりやすい
* wrap/unwrap時にオーバーヘッドが存在しない
* 定義が楽
* DBライブラリやJSONライブラリなどで変換ロジックを書く必要がある
* Boxing/Unboxingが発生する
@mauhiz

This comment has been minimized.

Show comment
Hide comment
@xuwei-k

This comment has been minimized.

Show comment
Hide comment
@xuwei-k

xuwei-k Jul 24, 2015

Tagged Type も AnyVal に tag 付ける場合は boxing 発生します

xuwei-k commented Jul 24, 2015

Tagged Type も AnyVal に tag 付ける場合は boxing 発生します

@gakuzzzz

This comment has been minimized.

Show comment
Hide comment
@gakuzzzz

gakuzzzz Jul 24, 2015

@mauhiz

今の Scalaz の TaggedType は昔と違って Shapeless の newtype と同じように元クラスのメソッドが呼べないようになってるのでやってる事は殆ど一緒ですね。
TaggedTypeに比べて追加でメソッドを生やすのが多少楽に書けるようになっている、ぐらいの認識です。

@xuwei-k

あ、値の作成時のオーバーヘッドしか気にして無くてBoxing/Unboxingのコスト忘れてました。
こちら足しますね。
ありがとうございます。
https://github.com/scalaz/scalaz/blob/6e807315ecbb925e0085310c5955640fee39da78/core/src/main/scala/scalaz/Tag.scala#L8 悲しい

Owner

gakuzzzz commented Jul 24, 2015

@mauhiz

今の Scalaz の TaggedType は昔と違って Shapeless の newtype と同じように元クラスのメソッドが呼べないようになってるのでやってる事は殆ど一緒ですね。
TaggedTypeに比べて追加でメソッドを生やすのが多少楽に書けるようになっている、ぐらいの認識です。

@xuwei-k

あ、値の作成時のオーバーヘッドしか気にして無くてBoxing/Unboxingのコスト忘れてました。
こちら足しますね。
ありがとうございます。
https://github.com/scalaz/scalaz/blob/6e807315ecbb925e0085310c5955640fee39da78/core/src/main/scala/scalaz/Tag.scala#L8 悲しい

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment