### Tagged type

「すごいHaskellたのしく学ぼう」の本を持ってるひとは新しい章に進める。モノイドだ。ウェブサイトを読んでるひとは Functors, Applicative Functors and Monoids の続きだ。

LYAHFGG:

これは Haskell の言語レベルでの機能なので、Scala に移植するのは無理なんじゃないかと思うと思う。 ところが、約1年前 (2011年9月) Miles Sabin さん (@milessabin)gist を書き、それを `Tagged` と名付け、Jason Zaugg さん (@retronym)`@@` という型エイリアスを加えた。

``````type Tagged[U] = { type Tag = U }
type @@[T, U] = T with Tagged[U]
``````

``````case class KiloGram(value: Double)
``````

``````scala> sealed trait KiloGram
defined trait KiloGram

scala> def KiloGram[A](a: A): A @@ KiloGram = Tag[A, KiloGram](a)
KiloGram: [A](a: A)scalaz.@@[A,KiloGram]

scala> val mass = KiloGram(20.0)
mass: scalaz.@@[Double,KiloGram] = 20.0

scala> 2 * Tag.unwrap(mass) // this doesn't work on REPL
res2: Double = 40.0

scala> 2 * Tag.unwrap(mass)
<console>:17: error: wrong number of type parameters for method unwrap\$mDc\$sp: [T](a: Object{type Tag = T; type Self = Double})Double
2 * Tag.unwrap(mass)
^

scala> 2 * scalaz.Tag.unsubst[Double, Id, KiloGram](mass)
res2: Double = 40.0
``````

``````scala> sealed trait JoulePerKiloGram
defined trait JoulePerKiloGram

scala> def JoulePerKiloGram[A](a: A): A @@ JoulePerKiloGram = Tag[A, JoulePerKiloGram](a)
JoulePerKiloGram: [A](a: A)scalaz.@@[A,JoulePerKiloGram]

scala> def energyR(m: Double @@ KiloGram): Double @@ JoulePerKiloGram =
JoulePerKiloGram(299792458.0 * 299792458.0 * Tag.unsubst[Double, Id, KiloGram](m))
energyR: (m: scalaz.@@[Double,KiloGram])scalaz.@@[Double,JoulePerKiloGram]

scala> energyR(mass)
res4: scalaz.@@[Double,JoulePerKiloGram] = 1.79751035747363533E18

scala> energyR(10.0)
<console>:18: error: type mismatch;
found   : Double(10.0)
required: scalaz.@@[Double,KiloGram]
(which expands to)  AnyRef{type Tag = KiloGram; type Self = Double}
energyR(10.0)
^
``````