!SLIDE
!SLIDE
任意のF[_]から作れる
case class Container[A](value: A)
val zero = Coyoneda(Container(0))
!SLIDE
CoyonedaはFunctor
val one = zero.map(_ + 1)
!SLIDE
つまり任意のF[_]がFunctorになるヤッター!?
!SLIDE
これは種も仕掛けもある
abstract class Coyoneda[F[_], A] {
type I
def fi: F[I]
def k(i: I): A
def run(implicit F: Functor[F]): F[A] = F.map(fi)(k)
}
!SLIDE
結局F[_]を取り出すときにFunctorが要るじゃないですかヤダー
〜完〜
!SLIDE
任意のFunctor[F]からMonad[F]が作れる。
case class Pair[A](left: A, right: A)
object Pair {
implicit def functor = new Functor[Pair] {
def map[A, B](pair: Pair[A])(f: A => B): Pair[B] =
Pair(f(pair.left), f(pair.right))
}
}
type BinaryTree[A] = Free[Pair, A]
val btree = Free.Suspend[Pair, Int](Pair(Free.Suspend[Pair, Int](Pair(Free.Return(0), Free.Return(1))), Free.Return(2)))
val result = for {
a <- btree
b <- btree
} yield (a, b)
result.runM { case Pair(l, r) => List(l, r) } assert_=== List((0,0), (0,1), (0,2), (1,0), (1,1), (1,2), (2,0), (2,1), (2,2))
!SLIDE
つまりCoyonedaと合わせれば任意のF[_]がMonadになるヤッター!?
!SLIDE
Emulator Monad
State+Maybe+IO
!SLIDE
sealed trait Action[A] {
def apply(state: State): Result[A]
}
!SLIDE
case class State(name: String, config: Config, updates: List[Option[Date]])
sealed trait Result[+A]
case class Success[A](value: A, state: State) extends Result[A]
case object Failure extends Result[Nothing]
!SLIDE
case object Read extends Action[State] {
def apply(state: State) = Success(state, state)
}
case class Write(state: State) extends Action[Unit] {
def apply(s: State) = Success((), state)
}
!SLIDE
case object Capture extends Action[BufferedImage] {
def apply(state: State) = Success(Emulator.robot(state.config).createScreenCapture(Emulator.bounds), state)
}
!SLIDE
case class Find(image: BufferedImage, screen: BufferedImage) extends Action[Point] {
def apply(state: State) = {
val points = for {
points <- for {
ax <- 0.to(screen.getWidth - image.getWidth).view
ay <- 0.to(screen.getHeight - image.getHeight).view
} yield for {
bx <- 0.until(image.getWidth).view
by <- 0.until(image.getHeight).view
} yield (ax, ay) -> (bx, by)
if points forall {
case ((ax, ay), (bx, by)) =>
screen.getRGB(ax + bx, ay + by) == image.getRGB(bx, by)
}
((x, y), _) <- points
} yield (x + image.getWidth / 2, y + image.getHeight / 2)
points.headOption.map(Success(_, state)) getOrElse Failure
}
}
!SLIDE
case class Click(point: Point) extends Action[Unit] {
def apply(state: State) = {
val robot = Emulator.robot(state.config)
val (x, y) = point
import robot._
mouseMove((Emulator.bounds.getWidth / 2).toInt, (Emulator.bounds.getHeight / 2).toInt)
mouseMove(x, y)
mousePress(InputEvent.BUTTON1_MASK)
mouseRelease(InputEvent.BUTTON1_MASK)
mouseMove(0, 0)
Success((), state)
}
}
!SLIDE
type Program[A] = Free.FreeC[Action, A]
type CoyoAction[A] = Coyoneda[Action, A]
implicit def monad = Free.freeMonad[CoyoAction]
implicit def lift[A](action: Action[A]) = Free.liftF[CoyoAction, A](Coyoneda(action))
!SLIDE
def run[A](program: Program[A], state: State, default: => A): A =
program.resume match {
case -\/(coyo) =>
coyo.fi(state) match {
case Success(value, state) => run(coyo.k(value), state, default)
case Failure => default
}
case \/-(value) => value
}
!SLIDE
def find(image: BufferedImage) =
for {
screen <- Capture
point <- Find(image, screen)
} yield point
def click(point: Point): Program[Unit] = Click(point)
def click(image: BufferedImage): Program[Unit] =
for {
point <- find(image)
_ <- Click(point)
} yield ()
!SLIDE
def or[A](a: Program[A], b: => Program[A]) =
new Action[A] {
def apply(state: State) =
run(a.map(Success(_, state): Result[A]), state, run(b.map(Success(_, state): Result[A]), state, Failure))
}
!SLIDE
結局runでいろいろ書く。
runの書き方が複数あるので場合によっては楽に書ける。(runM, foldMap, foldRun)
Monadを直接書くよりは楽?
Freeが難しい。