├── .gitignore └── src └── main └── scala └── com └── feynmanliang └── logo ├── PenInterpreterId.scala ├── InterpreterId.scala ├── InterpretOpt.scala ├── Computations.scala ├── App.scala └── Logo.scala /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | -------------------------------------------------------------------------------- /src/main/scala/com/feynmanliang/logo/PenInterpreterId.scala: -------------------------------------------------------------------------------- 1 | package com.feynmanliang.logo 2 | 3 | import cats.{Id,~>} 4 | 5 | import Logo._ 6 | 7 | object PenInterpreterId extends (PencilInstruction ~> Id) { 8 | def apply[A](fa: PencilInstruction[A]): Id[A] = fa match { 9 | case PencilDown(p) => println(s"start drawing at $p") 10 | case PencilUp(p) => println(s"stop drawing at $p") 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/scala/com/feynmanliang/logo/InterpreterId.scala: -------------------------------------------------------------------------------- 1 | package com.feynmanliang.logo 2 | 3 | import cats.{Id,~>} 4 | 5 | import Logo._ 6 | 7 | object InterpreterId extends (Instruction ~> Id) { 8 | import Computations._ 9 | 10 | override def apply[A](fa: Instruction[A]): Id[A] = fa match { 11 | case Forward(p, length) => forward(p, length) 12 | case Backward(p, length) => backward(p, length) 13 | case RotateLeft(p, degree) => left(p, degree) 14 | case RotateRight(p, degree) => right(p, degree) 15 | case ShowPosition(p) => println(s"showing position $p") 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/scala/com/feynmanliang/logo/InterpretOpt.scala: -------------------------------------------------------------------------------- 1 | package com.feynmanliang.logo 2 | 3 | import cats.~> 4 | 5 | import Logo._ 6 | 7 | object InterpretOpt extends (Instruction ~> Option) { 8 | import Computations._ 9 | val nonNegative: (Position) => Option[Position] = (p: Position) => { 10 | if (p.x >= 0 && p.y >= 0) Some(p) else None 11 | } 12 | 13 | override def apply[A](fa: Instruction[A]): Option[A] = fa match { 14 | case Forward(p, length) => nonNegative(forward(p, length)) 15 | case Backward(p, length) => nonNegative(backward(p, length)) 16 | case RotateLeft(p, degree) => Some(left(p, degree)) 17 | case RotateRight(p, degree) => Some(right(p, degree)) 18 | case ShowPosition(p) => Some(println(s"showing position $p")) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/scala/com/feynmanliang/logo/Computations.scala: -------------------------------------------------------------------------------- 1 | package com.feynmanliang.logo 2 | 3 | import scala.math 4 | 5 | import Logo._ 6 | 7 | object Computations { 8 | def forward(pos: Position, l: Int): Position = pos.copy( 9 | x=pos.x + l*math.cos(pos.heading.value * math.Pi/180.0), 10 | y=pos.y + l*math.sin(pos.heading.value * math.Pi/180.0)) 11 | 12 | def backward(pos: Position, l: Int): Position = pos.copy( 13 | x=pos.x - l*math.cos(pos.heading.value * math.Pi/180.0), 14 | y=pos.y - l*math.sin(pos.heading.value * math.Pi/180.0)) 15 | 16 | def left(pos: Position, d: Degree): Position = pos.copy( 17 | heading=Degree(pos.heading.value + d.value)) 18 | 19 | def right(pos: Position, d: Degree): Position = pos.copy( 20 | heading=Degree(pos.heading.value - d.value)) 21 | } 22 | -------------------------------------------------------------------------------- /src/main/scala/com/feynmanliang/logo/App.scala: -------------------------------------------------------------------------------- 1 | package com.feynmanliang.logo 2 | 3 | import cats.{Id,~>} 4 | import cats.implicits._ 5 | import cats.free.Free 6 | 7 | import Logo._ 8 | import Logo.dsl._ 9 | 10 | object App { 11 | def program(start: Position)(implicit M: Moves[LogoApp], P: PencilActions[LogoApp]): Free[LogoApp, Unit] = { 12 | import M._, P._ 13 | for { 14 | p1 <- forward(start, 10) 15 | p2 <- right(p1, Degree(90)) 16 | _ <- pencilUp(p2) 17 | p3 <- forward(p2, 10) 18 | _ <- pencilDown(p3) 19 | p4 <- backward(p3, 20) 20 | _ <- showPosition(p4) 21 | } yield () 22 | } 23 | 24 | def main(args: Array[String]):Unit = { 25 | val startPosition = Position(0.0, 0.0, Degree(0)) 26 | val interpreter: LogoApp ~> Id = InterpreterId or PenInterpreterId 27 | 28 | program(startPosition).foldMap(interpreter) // foldMap does trampolining 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/scala/com/feynmanliang/logo/Logo.scala: -------------------------------------------------------------------------------- 1 | package com.feynmanliang.logo 2 | 3 | import cats.data.Coproduct 4 | import cats.free.{Free, Inject} 5 | 6 | object Logo { 7 | type LogoApp[A] = Coproduct[Instruction, PencilInstruction, A] 8 | 9 | sealed trait Instruction[A] 10 | case class Forward(position: Position, length: Int) extends Instruction[Position] 11 | case class Backward(position: Position, length: Int) extends Instruction[Position] 12 | case class RotateLeft(position: Position, length: Degree) extends Instruction[Position] 13 | case class RotateRight(position: Position, length: Degree) extends Instruction[Position] 14 | case class ShowPosition(position: Position) extends Instruction[Unit] 15 | 16 | sealed trait PencilInstruction[A] 17 | case class PencilUp(position: Position) extends PencilInstruction[Unit] 18 | case class PencilDown(position: Position) extends PencilInstruction[Unit] 19 | 20 | case class Position(x: Double, y: Double, heading: Degree) 21 | case class Degree(private val d: Int) { 22 | val value = d % 360 23 | } 24 | 25 | object dsl { 26 | class Moves[F[_]](implicit I: Inject[Instruction, F]) { 27 | def forward(pos: Position, l: Int): Free[F, Position] = Free.inject[Instruction, F](Forward(pos, l)) 28 | def backward(pos: Position, l: Int): Free[F, Position] = Free.inject[Instruction, F](Backward(pos, l)) 29 | def left(pos: Position, l: Degree): Free[F, Position] = Free.inject[Instruction, F](RotateLeft(pos, l)) 30 | def right(pos: Position, l: Degree): Free[F, Position] = Free.inject[Instruction, F](RotateRight(pos, l)) 31 | def showPosition(pos: Position): Free[F, Unit] = Free.inject[Instruction, F](ShowPosition(pos)) 32 | } 33 | 34 | object Moves { 35 | implicit def moves[F[_]](implicit I: Inject[Instruction, F]): Moves[F] = new Moves[F] 36 | } 37 | 38 | class PencilActions[F[_]](implicit I: Inject[PencilInstruction, F]) { 39 | def pencilUp(pos: Position): Free[F, Unit] = Free.inject[PencilInstruction, F](PencilUp(pos)) 40 | def pencilDown(pos: Position): Free[F, Unit] = Free.inject[PencilInstruction, F](PencilDown(pos)) 41 | } 42 | 43 | object PencilActions { 44 | implicit def pencilActions[F[_]](implicit I: Inject[PencilInstruction, F]): PencilActions[F] = new PencilActions[F] 45 | } 46 | } 47 | } 48 | --------------------------------------------------------------------------------