Created
November 13, 2019 04:39
-
-
Save halcat0x15a/f069da664105c184d038823a3f970f11 to your computer and use it in GitHub Desktop.
rpscala第256回
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 値によって変わる型 | |
[@halcat0x15a](https://twitter.com/halcat0x15a) | |
--- | |
## 今日はなすネタ | |
面白そうなツイートを見つけた | |
https://twitter.com/Kory__3/status/1193510317541797889 | |
--- | |
## これはなにか | |
なにがわからないのか考える | |
```scala | |
def groupsLayoutFor(achievementCategory: AchievementCategory) | |
: Map[Int, AchievementGroupRepr[achievementCategory.type]] = | |
achievementCategory match { | |
case BrokenBlock => Map(... -> (BrokenBlockAmount, ...), ...) | |
case Building => Map() | |
case Login => Map(... -> (PlayTime, ...), ...) | |
} | |
``` | |
--- | |
## Singleton Types | |
ぱっと見てsingleton typesを使っているところに目がいく | |
```scala | |
scala> val a = "hoge" | |
a: String = hoge | |
scala> val b: a.type = "hoge" | |
<console>:12: error: type mismatch; | |
found : String("hoge") | |
required: a.type | |
val b: a.type = "hoge" | |
^ | |
scala> val b: a.type = a | |
b: a.type = hoge | |
``` | |
つまりこれは `achievementCategory` 自体を返す必要がある | |
--- | |
## なにがやりたいか | |
推察するに `achievementCategory` に対してパターンマッチした結果の型情報を保ったまま値を返したかったということが考えられる | |
--- | |
これをScalaでやるには型引数をとるデータ型へのパターンマッチをやる他ない | |
```scala | |
sealed abstract class AchievementCategory | |
case object BrokenBlock extends AchievementCategory | |
case object Login extends AchievementCategory | |
sealed abstract class AchievementGroup[+A] | |
case object BrokenBlockAmount extends AchievementGroup[BrokenBlock.type] | |
case object PlayTime extends AchievementGroup[Login.type] | |
def f[A <: AchievementCategory](category: A): AchievementGroup[A] = | |
category match { | |
case BrokenBlock => BrokenBlockAmount | |
case Login => PlayTime | |
} | |
``` | |
--- | |
引数で与える値によって型が変わっていることがわかる | |
```scala | |
scala> f(BrokenBlock) | |
res2: AchievementGroup[BrokenBlock.type] = BrokenBlockAmount | |
scala> f(Login) | |
res3: AchievementGroup[Login.type] = PlayTime | |
``` | |
これで本当に嬉しいのかはわからないが実現できた | |
--- | |
もう少しわかりやすい例を紹介する | |
```scala | |
sealed abstract class X[T] | |
case object I extends X[Int] | |
case object S extends X[String] | |
def f[A](x: X[A]): A = | |
x match { | |
case I => 1 | |
case S => "hoge" | |
} | |
``` | |
`A` を返す関数だがIntやStringを返している | |
これはパターンマッチによって `A` の型が変わっていることを意味する | |
--- | |
値によって型が変わっていることがわかる | |
```scala | |
scala> f(I) | |
res0: Int = 1 | |
scala> f(S) | |
res1: String = hoge | |
``` | |
--- | |
## GADTs | |
Scalaのデータ型はGADTsといえる | |
つまりコンストラクタに型が付けられる | |
--- | |
## ADTとの違い | |
例えばHaskellによる定義 | |
```haskell | |
data Option a = Some a | None | |
``` | |
普通のADTはこのような定義に対して `Some a :: Option a` `None :: Option a` という型が付く | |
Scalaはサブタイプもあるので複雑だが、例えば `None` は `Option[Nothing]` という型が付く | |
--- | |
## 応用 | |
Effで使われる | |
https://www.slideshare.net/konn/freer-monads-more-extensible-effects-59411772 | |
--- | |
## おまけ | |
dependent method typesでは実現できないのか | |
```scala | |
sealed abstract class AchievementCategory { type Group <: AchievementGroup } | |
case object BrokenBlock extends AchievementCategory { type Group = BrokenBlockAmount.type } | |
case object Login extends AchievementCategory { type Group = PlayTime.type } | |
sealed abstract class AchievementGroup | |
case object BrokenBlockAmount extends AchievementGroup | |
case object PlayTime extends AchievementGroup | |
def f(category: AchievementCategory): category.Group = | |
category match { | |
case BrokenBlock => BrokenBlockAmount | |
case Login => PlayTime | |
} | |
``` | |
--- | |
`asInstanceOf` が必要になる | |
```scala | |
<pastie>:21: error: type mismatch; | |
found : BrokenBlockAmount.type | |
required: category.Group | |
case BrokenBlock => BrokenBlockAmount | |
^ | |
``` | |
パターンマッチは型パラメータのunifyしてくれるがtype memberは考慮されない | |
次期Scalaでは改善されるかもしれない | |
--- | |
## まとめ | |
- パターンマッチで型パラメータの型を変えられる | |
- type memberとパターンマッチは相性が悪い |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment