Scalaã§<:<ã¨ã=:=ã使ã£ãgeneralized type constraintsãã¹ã´ããã¦æåãã話
Scala2.8ãããPredefã«<:<ã¨ã=:=ã¨ããå®ç¾©ããã¦ãã¦ããããªãã ã?ã¨ãã¼ã£ã¨çåã ã£ã訳ã§ãããã§ãã¤ãã£ãã¼ã§è³ªåæãã¦ãããã£ã¨ç解ã§ãã¾ããã
æãã¦é ãã @ScalaTohoku ããã@okomok ããã@tioa ãããæãé£ããããã¾ãã!
"generalized type constraints"ã¨ããã¤ãã§ãåãã©ã¡ã¼ã¿ã«ä¸ããããåããç¹å®ã®æ¡ä»¶ãæºããå ´åã«ã®ã¿å¼ã³åºããã¡ã½ãããå®ç¾©ã§ããã¨ãããã®ã§ãããããã³ã³ãã¤ã«æã«éçã«ãã§ãã¯ããã!! ããã¯ã¹ã´ã!!
What do <:<, <%<, and =:= mean in Scala 2.8, and where are they documented? - Stack Overflow
=:=ã<:<ã<%<ã§ç¹å®ã®åã®ã¿å¼ã³åºããã¡ã½ãããå®ç¾©ãã
å ·ä½çãªä¾ã§èª¬æãã¾ãã以ä¸ã®ãããªåãã©ã¡ã¼ã¿Tãåãã¯ã©ã¹Cellãããã¾ãã
ãã®Cellã«ãTãIntã®å ´åã«ã®ã¿å¼ã³åºããã¡ã½ããincrementãå®ç¾©ãã¾ãã
incrementã¯ãCellãæã¤Intåã®å¤ã«+1ããå¤ãæã¤æ°ããCellãè¿ãã¡ã½ããã§ãã
case class Cell[T](v:T) { // TãIntåã®å ´åã«ã®ã¿å¼ã³åºãã def increment(implicit ev:T =:= Int ):Cell[Int] = Cell( v + 1 ) }
incrementã®å®ç¾©ã§implicit parameterãå®ç¾©ããã¦ãã¾ãã(implicit ev:T =:= Int )ã¨ããã®ã¯ãTãIntåã§ããå¿
è¦ãããã¨ããå¶ç´ã表ãã¾ãããã®ããã«ãåãã©ã¡ã¼ã¿ã«å¶ç´ãå®ç¾©ãããã¨ãã«ã¯ãimplicit parameterã§Predef.=:=ã<:<ãªã©ãå©ç¨ãã¦Tãã©ã®ãããªåã§ããã¹ãããå®ç¾©ããããã§ãã
å®éã«å®è¡ãã¦ã¿ã¾ãããã
scala> val c = Cell(99) c: Cell[Int] = Cell(99) scala> c.increment res27: Cell[Int] = Cell(100) scala> Cell("foo").increment <console>:9: error: could not find implicit value for parameter ev: =:=[java.lang.String,Int] Cell("foo").increment
Cellã«Intåã®å¤ãä¸ããå ´åã¯ãincrementãå¼ã³åºãã¾ãããStringåã®å¤ãä¸ããå ´åã¯incrementã®å¼ã³åºãã«å¯¾ãã¦ã³ã³ãã¤ã«ã¨ã©ã¼ãçºçãã¦ãã¾ãããã®ããã«ãç¹å®ã®æ¡ä»¶ãæºããå ´åã®ã¿å¼ã³åºããã¡ã½ãããå®ç¾©ã§ãã¦ããã¤åãæ¡ä»¶ãæºããã¦ããããã³ã³ãã¤ã«æã«éçã«ãã§ãã¯ã§ããã®ãã¹ã´ãã¨ããã§ããæåããã£!
ä»ã«ãå¶ç´ãæå®ãããã¨ãã§ãã¾ãã"T <:< Date"ã ã¨TãDateåããã®ãµãã¿ã¤ãã§ããå¿ è¦ãããã¨ããå¶ç´ã«ãªãã¾ãã"T <%< WrappedString"ã ã¨ãTã¯implicit conversionçã§TãWrappedStringã¨è¦ãªããå ´åã¨ããæå³ã§ã(Scalaã§ã¯Stringåã¯scala.collection.immutable.WrappedStringã«implicit conversionã§å¤æå¯è½ã§ã)
ããã»ã©ã®Cellã¯ã©ã¹ã«ãTãDateã¾ãã¯ãã®ãµãã¿ã¤ãã®ã¿å¼ã³åºããformatDateã¨ãTãWrappedStringã¨è¦ãªããå ´åã«å¼ã³åºããasIntã追å ãã¾ããã
case class Cell[T](v:T) { // TãIntåã®å ´åã«ã®ã¿å¼ã³åºãã def increment(implicit ev:T =:= Int ):Cell[Int] = Cell( v + 1 ) import java.text.SimpleDateFormat import java.util.Date // TãDateã¾ãã¯ãã®ãµãã¿ã¤ãã®ã¨ãã«å¼ã³åºãã def formatDate(implicit ev: T <:< java.util.Date ) = (new SimpleDateFormat("yyyy/MM/dd HH:mm:ss")).format(v) import scala.collection.immutable.WrappedString // Tãimplicit conversionçã§WrappedStringã¨è¦ãªããå ´åã«å¼ã³åºãã // toIntã¯WrappedStringãæã¤ã¡ã½ããã ããã³ã³ãã¤ã«ã¯éã def asInt(implicit ev: T <%< WrappedString) = v.toInt }
å®è¡ãã¦ã¿ã¾ãããã
// TãDateãªã®ã§formatDateãå¼ã¹ã scala> Cell( new java.util.Date).formatDate res38: java.lang.String = 2010/09/14 18:34:49 // java.sql.Timestampã¯Dateãç¶æ¿ããã®ã§formatDateãå¼ã¹ã scala> Cell( new java.sql.Timestamp( System.currentTimeMillis)).formatDate res40: java.lang.String = 2010/09/14 18:35:18 // TãDateã®å ´åã¯asIntã¯å¼ã¹ãªã scala> Cell( new java.util.Date).asInt <console>:10: error: could not find implicit value for parameter ev: <%<[java.util.Date,scala.collection.immutable.WrappedString] Cell( new java.util.Date).asInt // TãStringåãªãWrappedStringã¨è¦ãªããã®ã§asIntãå¼ã¹ã scala> Cell("123").asInt res43: Int = 123 // TãDateã§ã¯ãªãã®ã§formatDateã¯å¼ã¹ãªã scala> Cell("123").formatDate <console>:10: error: could not find implicit value for parameter ev: <:<[java.lang.String,java.util.Date] Cell("123").formatDate
ãã¦ããã®ãããªåã«å¿ããã¡ã½ããã®å®ç¾©ã§ãããScalaã®Collectionã§ã使ããã¦ãã¾ãã
ä¾ãã°ãTraversableLike#toMapã¯ãMapã«å¤æããé¢æ°ã§ãããã®é¢æ°ã¯ãä¾ãã°List[(String,Int)]ã®ãããªTuple2ãè¦ç´ ã«ãã¤Listãªã©ããMapã«å¤æãè¡ããã¨ãã§ãã¾ãããList[Int]ããã¯Mapã«å¤æã§ãã¾ããã
scala> Seq( ("a",1),("b",2) ).toMap res18: scala.collection.immutable.Map[java.lang.String,Int] = Map((a,1), (b,2)) scala> Seq(1,2).toMap <console>:12: error: could not find implicit value for parameter ev: <:<[Int,(T, U)] Seq(1,2).toMap
ããã¯ãTraversableLike#toMapã®å®ç¾©ã以ä¸ã®ããã«ãªã£ã¦ããããã§ãã
def toMap[T, U](implicit ev: A <:< (T, U)): Map[T, U]
ã¤ã¾ããtoMapã¯TraversableLikeãæã¤è¦ç´ ã®åAãã"(T, U)"ã¤ã¾ãTuple2ã§ããå ´åã®ã¿å¼ã³åºããã¡ã½ããã§ããã¨ãããã¨ã§ããSeq(1,2)ã¯åAãIntåã§ãTuple2ã§ã¯ãªãã®ã§ã¨ã©ã¼ã¨ãªã£ã¦ããã®ã§ãã
ãªãã§ãããªãã¨ãã§ãã¦ãã®?ãããã<:<ã¨ãã£ã¦ãªã«?
(ããã«æ¸ãã¦ãããã¨ã¯æ¨æ¸¬ãééããå¤ã ããããããã¾ãããããã³ãæè¿ãã¾ã!)
ããããã<:<ã¨ã=:=ã¨ãã£ã¦ãªãã§ãããã?
scala.Predefã«ã以ä¸ã®ããã«å®ç¾©ããã¦ãã¾ãã
sealed abstract class <:<[-From, +To] extends (From => To) implicit def conforms[A]: A <:< A = new (A <:< A) {def apply(x: A) = x} sealed abstract class =:=[From, To] extends (From => To) object =:= { implicit def tpEquals[A]: A =:= A = new (A =:= A) {def apply(x: A) = x} } sealed abstract class <%<[-From, +To] extends (From => To) object <%< { implicit def conformsOrViewsAs[A <% B, B]: A <%< B = new (A <%< B) {def apply(x: A) = x} }
<:<ãªã©ã¯åãã©ã¡ã¼ã¿From,toãåãæ½è±¡ã¯ã©ã¹ã§ã(Form => To)ãç¶æ¿ããé¢æ°ã®ããã§ãã
ããã ãã ã¨ããããããªãã®ã§ã以ä¸ã®ãããªCell.scalaãç¨æãã¦ãå®éã«ã¯ã©ã®ãããªå¼ã³åºããè¡ãããã®ã調ã¹ã¦ã¿ã¾ãã
case class Cell[T](v:T) { // TãIntåã®å ´åã«ã®ã¿å¼ã³åºãã def increment(implicit ev:T =:= Int ):Cell[Int] = Cell( v + 1 ) import java.text.SimpleDateFormat import java.util.Date // TãDateã¾ãã¯ãã®ãµãã¿ã¤ãã®ã¨ãã«å¼ã³åºãã def formatDate(implicit ev: T <:< java.util.Date ) = (new SimpleDateFormat("yyyy/MM/dd HH:mm:ss")).format(v) import scala.collection.immutable.WrappedString // Tãimplicit conversionçã§WrappedStringã¨è¦ãªããå ´åã«å¼ã³åºãã // toIntã¯WrappedStringãæã¤ã¡ã½ããã ããã³ã³ãã¤ã«ã¯éã def asInt(implicit ev: T <%< WrappedString) = v.toInt } object Main { def main(args:Array[String]) = { println( Cell(99).increment ) println( Cell( new java.util.Date).formatDate ) println( Cell( new java.sql.Timestamp( System.currentTimeMillis)).formatDate ) println( Cell("123").asInt ) } }
ãã®Cell.scalaãã"scalac -Xprint:typer Cell.scala "ã§ã³ã³ãã¤ã«ãã¦ã¿ã¦ãScalaã®ã³ã³ãã¤ã©ãã©ã®ãããªã³ã¼ããçæãã¦ãããè¦ã¦ã¿ã¾ããéè¦ãªç®æã®ã¿æç²ããã®ã以ä¸ã§ãã
@serializable case class Cell[T >: Nothing <: Any] extends java.lang.Object with ScalaObject with Product { def increment(implicit ev: =:=[T,Int]): Cell[Int] = Cell.apply[Int](ev.apply(Cell.this.v).+(1)); def formatDate(implicit ev: <:<[T,java.util.Date]): java.lang.String = new java.text.SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(Cell.this.v); def asInt(implicit ev: <%<[T,scala.collection.immutable.WrappedString]): Int = ev.apply(Cell.this.v).toInt; }; final object Main extends java.lang.Object with ScalaObject { def main(args: Array[String]): Unit = { scala.this.Predef.println(Cell.apply[Int](99).increment(scala.this.Predef.=:=.tpEquals[Int])); scala.this.Predef.println(Cell.apply[java.util.Date](new java.util.Date()).formatDate(scala.this.Predef.conforms[java.util.Date])); scala.this.Predef.println(Cell.apply[java.sql.Timestamp](new java.sql.Timestamp(java.this.lang.System.currentTimeMillis())).formatDate(scala.this.Predef.conforms[java.sql.Timestamp])); scala.this.Predef.println(Cell.apply[java.lang.String]("123").asInt(scala.this.Predef.<%<.conformsOrViewsAs[java.lang.String, scala.collection.immutable.WrappedString]({ ((s: String) => scala.this.Predef.wrapString(s)) }))) } }; }
incrementãå¼ã³åºãã¦ããç®æã§ã¯ã以ä¸ã®ãããªã³ã¼ãã«ãªã£ã¦ãã¾ãã
scala.this.Predef.println(Cell.apply[Int](99).increment(scala.this.Predef.=:=.tpEquals[Int]));
Predef.=:=.tpEquals[Int]ãincrementã«æ¸¡ãã¦ãã¾ããtpEqualsã¯æ¸¡ãããå¤ããã®ã¾ã¾è¿ãã ãã®é¢æ°ã§ãããã®å ´åã¯Intãåãã¨ã£ã¦Intãè¿ãé¢æ°ãªããã§ãã
incrementã®å é¨ã§ã¯ãå¼æ°ã®implicit parameterã§æ¸¡ãããev: =:=[T,Int]ã®applyã«Cellããã¤vã渡ãã¦ãã¾ãã
ããã§ã¯Tã¯Intåãªã®ã§Predef.=:=.tpEquals[Int]ãå¼æ°ã«incrementãå¼ã³åºããã¨ã¯å¦¥å½ã§ãããä»®ã«TãStringã ã¨ããããPredef.=:=.tpEquals[String]ãå¼æ°ã«icrementãå¼ã³åºããã¨ã«ãªã£ã¦ãã¾ããincrementã®å®ç¾©ã¨çç¾ããããã§ããã
<:<ãåæ§ã§ããããã®å ´åã¯Tã¯Dateåã®ãµãã¿ã¤ãã§ããã°ããã®ã§ãtpEqualsã®ãããªåã®åä¸æ§ããã§ãã¯ããé¢æ°ãã¯ãã¾ãªãã¦ãæ®éã®T => Dateåã®é¢æ°ã§ããããã§ãã
<%<ã«é¢ãã¦ã¯ãã¡ãã£ã¨äºæ ããã¨ãªãã<%<[-From,+To].conformsOrViewsAsã«ãã³ã³ãã¤ã©ã解決ããimplicit conversionã«ããå¤æãè¡ãé¢æ°ã渡ãã¦ãã¾ãã
scala.this.Predef.println(Cell.apply[java.lang.String]("123").asInt(scala.this.Predef.<%<.conformsOrViewsAs[java.lang.String, scala.collection.immutable.WrappedString]({ ((s: String) => scala.this.Predef.wrapString(s)) })))
ããã«ãããå¼ã³åºãå ã®asIntã§ã¯ãimplicit parameterã§æ¸¡ããã"ev: <%<[T,scala.collection.immutable.WrappedString]"ã¨ããé¢æ°ã«vã渡ããã¨ã§ãStringããWrappedStringã®å¤æãè¡ããã¦ãããã¨ããããã§ãã