shapeless - Sized

## Sizedとは

Listとかをラップして静的な長さを持つもの。

```scala:Sizedの定義
final class Sized[+Repr, L <: Nat] private (val unsized : Repr) extends AnyVal {
  override def toString = unsized.toString
}
```

Reprはコレクションの型で、Lは要素の数(Nat)だ。Natに関しては[こっちに記事](/ryoppy/items/b087ffec282a5f0d121f)書いてある。

試しに使ってみる。

```scala
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
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
scala> Sized[List](1,2,3)
//res25: shapeless.Sized[List[Int],shapeless.nat._3] = List(1, 2, 3)
```

文字列の長さにも対応している。

```scala
scala> Sized.wrap[String, nat._3]("abc")
//res26: shapeless.Sized[String,shapeless.nat._3] = abc
```

指定の長さかチェックしてOptionで返すことも。

```scala
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
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](https://github.com/milessabin/shapeless/blob/master/examples/src/main/scala/shapeless/examples/sized.scala)には、csvのヘッダとボディで列の数が同じかを保証できるようになっている。
例を抜き出してみた。

```scala
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の長さが同じでないとコンパイルエラーになる。

便利ですね(∩´ᵕ`∩)