Scalaã¡ã¿ããã°ã©ãã³ã°ä»æç©èª - å°å ¥ç·¨
ãä¹ ãã¶ãã§ããããã¯scala advent calendar 2017ã®2æ¥ç®ã®è¨äºã§ãï¼ã¨ãããã¨ã«ãã¾ãã空ãã¦ããã®ã§ç©´åãï¼ã åæ¥ã¯poad1010ããã®JupyterでScala - Qiitaãç¿æ¥ã¯okapiesããã®Akka HTTP クライアントを使う - Okapies' Archiveã§ããã
ãã¦ããã®è¨äºã¯æ¬æ¥23æ¥ç®ã«äºå®ãã¦ããç§ã®ã¢ããã³ãã«ã¬ã³ãã¼ããã¾ãã«ã大é·ç·¨ã«ãªããããªãã¨ãããã£ãã®ã§åå¾ç·¨ã«åå²ããååé¨åã§ãã Scalaã®ã¡ã¿ããã°ã©ãã³ã°å¨è¾ºã®è©±ãããã«å½ãã£ã¦æä½éå¿ è¦ã«ãªãç¨èªãæ´çãã¾ãã ã²ã¨ã¤ã®ã¾ã¨ã¾ãã®ããè¨äºã¨ããããã¯ãè¾å ¸ã¨ãã¦ã使ããã ããã 23æ¥ã®è¨äºãããé©å®åç §ã¨ãã¦ä½¿ãã¾ãã
- ãã®åã«å®£ä¼
- ãªãã¬ã¯ã·ã§ã³
- æ½è±¡æ§ææ¨
- ã¬ã¤ãã£ã±ã¼ã·ã§ã³
- æºã¯ãªã¼ã
- ååã¨ã·ã³ãã«
ãã®åã«å®£ä¼
ScalaMatsuri2018ã«ãScalaã¡ã¿ããã°ã©ãã³ã°ä»æç©èªãã¨ããã¿ã¤ãã«ã§CFPãæåºãã¾ããããã²çããæ票ããé¡ããããã¾ãã
ã§ã¯å§ãã¾ãããã
ãªãã¬ã¯ã·ã§ã³
ãªãã¬ã¯ã·ã§ã³ (reflection) ã¨ã¯ãããã°ã©ã ãèªèº«ãå½¢å¼çã«è§£éï¼æ³¨ï¼å®è¡ã¨ã¯ç°ãªãï¼ããã¾ãã¯å¤æ´ãå ãããã¨ãã§ããè½å*1ã§ãã ã¤ã¾ãããããã°ã©ã ãå¤æ´ã§ããããã°ã©ã ã®è¨è¿°ã=ãã¡ã¿ããã°ã©ãã³ã°ãã«å¿ è¦ãªè¨èªæ©è½ããªãã¬ã¯ã·ã§ã³ã§ãã ãªãã¬ã¯ã·ã§ã³ã«ã¯å®è¡æãªãã¬ã¯ã·ã§ã³ã¨ã³ã³ãã¤ã«æãªãã¬ã¯ã·ã§ã³ãåå¨ããæ§ã ãªããã°ã©ãã³ã°è¨èªãããããã®å²å¦ã«åºã¥ãã¦ãããã®ä¸æ¹ã¾ãã¯ä¸¡æ¹ãå®è£ ãã¦ãã¾ãã
å®è¡æãªãã¬ã¯ã·ã§ã³ã¯ããã°ã©ã å®è¡æã«ã©ã³ã¿ã¤ã ã«åå¨ããå¤æ°ãé¢æ°ãæä½ãããã®ã§ãã主ã«ã¤ã³ã¿ããªã¿åã®è¨èªã§ï¼ç義ã®ï¼ã¡ã¿ããã°ã©ãã³ã°ã¨å¼ã°ãããã®ãããã«å½ããã¾ããJVMãæ´å²çã«ã¯ã¤ã³ã¿ããªã¿ã¨ãã¦éçºããã¦ããããããã£ã¦JVMè¨èªã§ããscalaãå®è¡æãªãã¬ã¯ã·ã§ã³ã®æ©è½ãæã¡ã¾ãï¼â»ããèªå¼ãããã23æ¥ã®è¨äºã§è©³è¿°ãã¾ãï¼ã
ä¸æ¹ãã³ã³ãã¤ã«æãªãã¬ã¯ã·ã§ã³ã¯ãéçè¨èªã«ããã¦ã³ã³ãã¤ã©ãããã°ã©ã ã®æååãã®ãã®ãããã¯æ½è±¡æ§ææ¨ãæä½ãããã®ã§ãä¸è¬ã«ãã¯ã (macro) ã¨å¼ã°ãã¾ãã
Cç³»ã®è¨èªã§#define
ãç¨ãã¦å®ç¾©ããããã®ã¯åãªãæååç½®æ*2ã§ãããscalaã®ãã¯ãã§ã¯æ½è±¡æ§ææ¨ãæä½ãã¾ãã
æ½è±¡æ§ææ¨
æ½è±¡æ§ææ¨ (abstract syntax tree; AST) ã¯ãããã¹ãã¨ãã¦è¨è¿°ãããããã°ã©ã ããããæå³ã®åããããæ§é ããã£ãæ¨ã¨ãã¦è¡¨ç¾ãããã®ã§ãã ã»ã¼ããããããã°ã©ãã³ã°è¨èªã®å¦çç³»ãæåã«è¡ãå¦çã¯ãåãªãããã¹ãã§ããããã°ã©ã ã解æ (parse) ããASTã«å¤æããä½æ¥ã§ããã¨è¨ã£ã¦ãéè¨ã§ã¯ããã¾ããã
ä¾ãã°ã
if (str.startsWith("abc")) 1 else 0
ã®ãããªã³ã¼ãã¯ãä¸å³ã®ãããªæ½è±¡æ§ææ¨ã«å¤æã§ãã¾ãã
scalaã®ãªãã¬ã¯ã·ã§ã³ã§ã¯ASTãcase classã使ã£ã¦è¡¨è¨ãããããä¸è¨ã®ä¾ã¯
case class Expr(...) case class IfExpr(condExpr: Expr, thenExpr: Expr, elseExpr: Option[Expr]) val sample = IfExpr(<str.startsWith("abc")>, <1>, Some(<0>))
ã¨ãªãã¾ããelse
ç¯ã¯ãã£ã¦ããªãã¦ãåæ³ãªæ§æãªã®ã§ãcase classã®ãã£ã¼ã«ãã¨ãã¦ãOption
åã§è¡¨ãã¦ãã¾ãã
注 ãã®case classã¯ããã¾ã§èª¬æã®ããã®ä¾ã§ãããå®éã«scalaã®ãªãã¬ã¯ã·ã§ã³ã§ä½¿ããã¦ãããã®ã¨ã¯ç°ãªãã¾ããã¾ã<>
ã¯ã<>
ã§å²ã¾ããä¸èº«ã®ã³ã¼ãã®ASTãã表ãããã®çç¥è¨æ³ã¨ãã¦ç¨ãã¾ãã
ãã¦ãããã§éè¦ãªãã¨ã¯ãç価ãªã³ã¼ãã表ãããã®ASTã¯ä¸éãã«å®ã¾ããªãã¨ãããã¨ã§ãã ä¾ãã°ãä¸è¨ã®ä¾ã¯ä»¥ä¸ã®ããã«ã表ããã¨ãã§ãã¾ãã
case class ElseClause(expr: Expr) case class IfExpr(condExpr: Expr, thenExpr: Expr, elseClause: Option[ElseClause]) val sample = IfExpr(<str.startsWith("abc")>, <1>, Some(ElseClause(<0>)))
ãã®ä¾ã§ã¯ElseClause
ãç¬ç«ããææ³åä½ã¨ãã¦åãåºããIfExpr
ã¯Option[ElseClause]
ãæã¤ã¨ããæ§é ã«ãªã£ã¦ãã¾ãã
ã³ã³ãã¤ã©ã®å®è£
ã«éãã¦else
ãç¬ç«ãã¦æ±ãã»ããé½åãè¯ãã¨ããäºæ
ãããã°ãã®ãããªè¡¨ç¾ãå¯è½ã§ãããã¨ãããã¨ããããã¾ãã
ãã®ãç°ãªãASTãåä¸ã®ã³ã¼ãã¨å¯¾å¿ãå¾ããã¨ããäºå®ã¯23æ¥ã®è¨äºã§å¤§å¤éè¦ã«ãªãã¾ãã®ã§ãé ã®çé ã«å ¥ãã¦ããã¦ãã ããã
ã¬ã¤ãã£ã±ã¼ã·ã§ã³
ã¬ã¤ãã£ã±ã¼ã·ã§ã³ (reification) ã¨ã¯ãããã¹ãã¨ãã¦è¨è¿°ãããããã°ã©ã ã解æããASTãæ§ç¯ããæç¶ããæãã¾ããåè©ã¯reifyã§ãã
æºã¯ãªã¼ã
æºã¯ãªã¼ã (quasiquote)ã¨ã¯ãæ½è±¡æ§ææ¨ãæä½ããããã°ã©ã ã®è¨è¿°ã簡便ã«ããããã«å°å
¥ãããè¨æ³ã§ãä¸ç¨®ã®æ§æç³è¡£ã§ã*3ã
å
·ä½çã«ã¯q"..."
ã¨ããå½¢å¼ã®StringContext
*4ã¨ãã¦å®è£
ããã¦ããã
æååã¨ãã¦è¨è¿°ããã³ã¼ããreifyãããã®ãè¿ãã¾ãã
注 以ä¸ã®èª¬æã§ã¯scala 2.10ã§å°å
¥ãããscala.reflect
ã§å®è£
ãããæºã¯ãªã¼ãã«åºã¥ãã¦è©±ãé²ãã¾ãããå¾çºä¸ä»£ã®ã¡ã¿ããã°ã©ãã³ã°ç°å¢ã§ãåçã®æºã¯ãªã¼ããå®è£
ããã¦ãã¾ãã
ãªãã以ä¸ã®ã³ã¼ããµã³ãã«ã¯scalaã®REPLã§å®è¡å¯è½ã§ãã
ã¤ã¾ãã
import scala.reflect.runtime.universe._ val expr = q"1 + 1" // 1 + 1ã表ãAST
ã¨è¨è¿°ã§ããããã大å¤ä¾¿å©ã§ããå®éãåçã®expr
ãæºã¯ãªã¼ããç¨ããã«è¨è¿°ãããã¨ããã¨
import scala.reflect.runtime.universe._ val expr = Apply(Select(Literal(Constant(1)), TermName("+")), List(Literal(Constant(1))))
ã¨ãªãã¾ãããã®ç¨åº¦ãªãã¾ã ææ ¢ã§ãã¾ãããé·ããªãã¨ãã£ã¦ããã¾ããã
ããã«ãæºã¯ãªã¼ãã¯ãã¿ã¼ã³ãããã®ããã«ãç¨ãããã¨ãã§ãã¾ãã
import scala.reflect.runtime.universe._ val expr = q"10 + 1" val q"$numA + 1" = expr println(numA) // => 10 å®éã«ã¯ Literal(Constant(10))
ããã¯ç»æçãªãã¨ã§ããã¯ããªã©ãä½ãéãASTããå¿ è¦ãªæ å ±ãææ©ãæãåºãã¦ããããã«æ¥µãã¦ä¾¿å©ã§ãã
å¼æ°ãªã©ãææ³ä¸å¯å¤é·ã§ãããã¨ã許ããã¦ããæ§é ãList[Tree]
ã¨ãã¦æãåºããã¨ãå¯è½ã§ãã
import scala.reflect.runtime.universe._ val expr1 = q"List(1, 2, 3)" val q"List(..$args)" = expr1 // dot2㤠println(args) // => List(1, 2, 3): List[Literal] val expr2 = q"hoge(1, 2, 3)(curried)" val q"hoge(...$argss)" = expr2 // dot3ã¤ã§ã«ãªã¼å println(argss) // => List(List(1, 2, 3), List("curried")): List[List[Tree]]
ååã¨ã·ã³ãã«
åå (name) ã¨ã¯ãASTã®ä¸ã«åå¨ããç¹å®ã®ãã¼ãã表ãããã®èå¥åã¨ãã¦ç¨ããããæååã§ãã åãªãæååã§ããããã以ä¸ã®æ å ±ã¯æã¡ã¾ããã
ã·ã³ãã« (symbol) ã¨ã¯ãååã¨ãã®ååãåç §ããã¯ã©ã¹ãã¡ã½ããã®ãããªå®ä½ (entity) ãé¢é£ä»ããããã«ç¨ãããã*5æ¦å¿µã§ãã ã¤ã¾ããASTå ã§ä½ããã®ååãæã¤å¤æ°ãé¢æ°ã«å¯¾ãã¦ãåãã¯ããã¨ããå種ã®æ å ±ãä»å ããããã«å¿ è¦ãªããªãã¸ã¨è¨ãã¾ãã
ä¾ãã°
val str = "abc"
ã¨ããã³ã¼ããæ¸ããã¨ãã¾ãã
ãã®ã³ã¼ããASTã«å¤æããã¨ãstr
ã¨ãããååããåãåºããã¾ãããã®æç¹ã§ã¯ååã ãã§ãã®ã§ãã³ã³ãã¤ã©ã¯str
ãã©ã®ãããªæ§è³ªãæã¤ã®ãç¥ãã¾ããã
ããããã³ã³ãã¤ã©ã¯åæ¨è«è½åãæã¤ãããå³è¾ºã®"abc"
ã«ããstr
ãString
åã§ããã¨æ±ºå®ãããã¨ãã§ãã¾ãã
ãããã£ã¦ãã³ã³ãã¤ã©ã¯ååstr
ã«å¯¾å¿ããã·ã³ãã«ãä½æããããã«String
ã¨ããåæ
å ±ã注è¨ããããã§ãã
ãã¦ã以ä¸ã§å°å ¥ç·¨ã¯çµäºã§ãããã¡ãããã®è¨äºã®å 容ãå ¨ã¦ã¨ããããã§ã¯ããã¾ããããscalaã®ã¡ã¿ããã°ã©ãã³ã°ã«é¢ããè°è«ã®å¤ããç解ããã®ã«å¿ è¦ååãªå 容ã¯æãã¦ããã¨æãã¾ãã ããã§ã¯23æ¥ãæ¬ç·¨ã§ãç®ã«ãããã¾ãã
*1:http://docs.scala-lang.org/ja/overviews/reflection/overview.html
*2:https://qiita.com/satoru_takeuchi/items/3769a644f7113f2c8040#%E3%83%9E%E3%82%AF%E3%83%AD%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%99%E3%82%8B%E5%A0%B4%E6%89%80%E3%81%AB%E4%BE%9D%E5%AD%98%E3%81%99%E3%82%8B%E3%82%A8%E3%83%A9%E3%83%BC%E3%82%92%E9%98%B2%E3%81%90 ã®ä¾ãè¦ãã¨åãªãæååç½®æã§ãããã¨ã«èµ·å ããã¨ã©ã¼ãçºçãã¦ãããã¨ãããã
*3:http://docs.scala-lang.org/overviews/quasiquotes/intro.html
*4:https://docs.scala-lang.org/ja/overviews/core/string-interpolation.html
*5:http://docs.scala-lang.org/ja/overviews/reflection/symbols-trees-types.html