Sizedとは
Listとかをラップして静的な長さを持つもの。
Sizedの定義
final class Sized[+Repr, L <: Nat] private (val unsized : Repr) extends AnyVal {
override def toString = unsized.toString
}
Reprはコレクションの型で、Lは要素の数(Nat)だ。Natに関してはこっちに記事書いてある。
試しに使ってみる。
scala> import shapeless._
import shapeless._
scala> val s = Sized(1,2,3)
//s: shapeless.Sized[scala.collection.immutable.IndexedSeq[Int],shapeless.nat._3] = Vector(1, 2, 3)
scala> s(0)
//res1: Int = 1
scala> s(1)
//res2: Int = 2
scala> s(2)
//res3: Int = 3
scala> s(3)
//<console>:15: error: could not find implicit value for parameter diff: shapeless.ops.nat.Diff[shapeless.Succ[shapeless.Succ[shapeless.Succ[shapeless._0]]],shapeless.Succ[nat_$macro$4.N]]
// s(3)
// ^
静的に型を保持してるので、存在しないindexを取得するとコンパイルエラーに。
他にもいくつかメソッドが用意されている。
scala> s.head
//res8: Int = 1
scala> s.tail
//res9: shapeless.Sized[scala.collection.immutable.IndexedSeq[Int],shapeless.Succ[shapeless.Succ[shapeless._0]]] = Vector(2, 3)
scala> 0 +: s
//res10: shapeless.Sized[scala.collection.immutable.IndexedSeq[Int],shapeless.Succ[shapeless.Succ[shapeless.Succ[shapeless.Succ[shapeless._0]]]]] = Vector(0, 1, 2, 3)
scala> s.take(2)
//res11: shapeless.Sized[scala.collection.immutable.IndexedSeq[Int],shapeless.Succ[shapeless.Succ[shapeless._0]]] = Vector(1, 2)
ちゃんと型も指定できる。List/Array/Stream等大体対応している。
scala> Sized[List](1,2,3)
//res25: shapeless.Sized[List[Int],shapeless.nat._3] = List(1, 2, 3)
文字列の長さにも対応している。
scala> Sized.wrap[String, nat._3]("abc")
//res26: shapeless.Sized[String,shapeless.nat._3] = abc
指定の長さかチェックしてOptionで返すことも。
scala> import syntax.sized._
//import syntax.sized._
scala> List(1,2,3).sized(3)
//res37: Option[shapeless.Sized[List[Int],shapeless.Succ[shapeless.Succ[shapeless.Succ[shapeless._0]]]]] = Some(List(1, 2, 3))
scala> List(1,2,3).sized(4)
//res38: Option[shapeless.Sized[List[Int],shapeless.Succ[shapeless.Succ[shapeless.Succ[shapeless.Succ[shapeless._0]]]]]] = None
scala> "abc".sized(3)
//res39: Option[shapeless.Sized[String,shapeless.Succ[shapeless.Succ[shapeless.Succ[shapeless._0]]]]] = Some(abc)
scala> "abc".sized(4)
//res40: Option[shapeless.Sized[String,shapeless.Succ[shapeless.Succ[shapeless.Succ[shapeless.Succ[shapeless._0]]]]]] = None
あとHListやTuple等からも変換できる。
scala> (1::2::3::HNil).toSized[List]
//res42: shapeless.Sized[List[Int],shapeless.Succ[shapeless.Succ[shapeless.Succ[shapeless._0]]]] = List(1, 2, 3)
scala> import syntax.std.tuple._
//import syntax.std.tuple._
scala> (1,2,3).toSized[List]
//res43: shapeless.Sized[List[Int],shapeless.Succ[shapeless.Succ[shapeless.Succ[shapeless._0]]]] = List(1, 2, 3)
例
sizedのexampleには、csvのヘッダとボディで列の数が同じかを保証できるようになっている。
例を抜き出してみた。
scala> def csv[N <: Nat](hdrs : Sized[Seq[String], N], rows : List[Sized[Seq[String], N]]) = true
//csv: [N <: shapeless.Nat](hdrs: shapeless.Sized[Seq[String],N], rows: List[shapeless.Sized[Seq[String],N]])Boolean
scala> csv(Sized("id", "name"), List(Sized("1", "name1"), Sized("2", "name2")))
//res21: Boolean = true
scala> csv(Sized("id", "name"), List(Sized("1", "name1"), Sized("2")))
//<console>:16: error: no type parameters for method csv: (hdrs: shapeless.Sized[Seq[String],N], rows: List[shapeless.Sized[Seq[String],N]])Boolean exist so that it can be applied to arguments (shapeless.Sized[scala.collection.immutable.IndexedSeq[String],shapeless.nat._2], List[shapeless.Sized[scala.collection.immutable.IndexedSeq[String], _ >: shapeless.Succ[shapeless._0] with shapeless.Succ[shapeless.Succ[shapeless._0]] <: shapeless.Succ[_ >: shapeless._0 with shapeless.Succ[shapeless._0] <: Serializable with shapeless.Nat{type N >: shapeless._0 with shapeless.Succ[shapeless._0] <: Serializable with shapeless.Nat}]]])
Listの長さが同じでないとコンパイルエラーになる。
便利ですね(∩´ᵕ`∩)