|
| 1 | +/* |
| 2 | + * Copyright 2016-2017 47 Degrees, LLC. <http://www.47deg.com> |
| 3 | + * |
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | + * you may not use this file except in compliance with the License. |
| 6 | + * You may obtain a copy of the License at |
| 7 | + * |
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | + * |
| 10 | + * Unless required by applicable law or agreed to in writing, software |
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | + * See the License for the specific language governing permissions and |
| 14 | + * limitations under the License. |
| 15 | + */ |
| 16 | + |
| 17 | +import iotaz._ |
| 18 | + |
| 19 | +import scalaz._ |
| 20 | +import scalaz.effect._ |
| 21 | +import scalaz.syntax.apply._ |
| 22 | + |
| 23 | +import scala.Predef._ |
| 24 | + |
| 25 | +sealed trait TerminalOp[A] |
| 26 | +object TerminalOp { |
| 27 | + case object ReadLine extends TerminalOp[String] |
| 28 | + final case class WriteLine(msg: String) extends TerminalOp[Unit] |
| 29 | +} |
| 30 | + |
| 31 | +sealed trait CounterOp[A] |
| 32 | +object CounterOp { |
| 33 | + final case object Reset extends CounterOp[Unit] |
| 34 | + final case object Increment extends CounterOp[Unit] |
| 35 | + final case object Decrement extends CounterOp[Unit] |
| 36 | + final case object Read extends CounterOp[Long] |
| 37 | +} |
| 38 | + |
| 39 | +object FreeExample extends SafeApp { |
| 40 | + import TListK.:: |
| 41 | + |
| 42 | + type Algebra[A] = CopK[TerminalOp :: CounterOp :: TNilK, A] |
| 43 | + type FreeProgram[A] = Free[Algebra, A] |
| 44 | + type Program[A] = StateT[IO, Long, A] |
| 45 | + |
| 46 | + val terminalToIO: TerminalOp ~> IO = |
| 47 | + λ[TerminalOp ~> IO] { |
| 48 | + case TerminalOp.ReadLine => IO.readLn |
| 49 | + case TerminalOp.WriteLine(msg) => IO.putStrLn(msg) |
| 50 | + } |
| 51 | + val counterToState: CounterOp ~> State[Long, ?] = |
| 52 | + λ[CounterOp ~> State[Long, ?]] { |
| 53 | + case CounterOp.Reset => State.put(0) |
| 54 | + case CounterOp.Increment => State.modify(_ + 1) |
| 55 | + case CounterOp.Decrement => State.modify(_ - 1) |
| 56 | + case CounterOp.Read => State.get |
| 57 | + } |
| 58 | + |
| 59 | + val stateToProgram = λ[State[Long, ?] ~> Program](_.mapT(v => IO(v))) |
| 60 | + val ioToProgram = λ[IO ~> Program](io => StateT(s => io.map(s -> _))) |
| 61 | + val interpreter: Algebra ~> Program = |
| 62 | + CopK.NaturalTransformation.of[Algebra, Program]( |
| 63 | + counterToState andThen stateToProgram, |
| 64 | + terminalToIO andThen ioToProgram) |
| 65 | + |
| 66 | + implicit class AlgebraSyntax[F[_], A](fa: F[A])( |
| 67 | + implicit ev: CopK.Inject[F, Algebra] |
| 68 | + ) { |
| 69 | + def liftFree: Free[Algebra, A] = Free.liftF(fa).mapSuspension(ev.inj) |
| 70 | + } |
| 71 | + |
| 72 | + val terminate: FreeProgram[Unit] = Free.point(()) |
| 73 | + lazy val freeProgram: FreeProgram[Unit] = |
| 74 | + for { |
| 75 | + value <- CounterOp.Read.liftFree |
| 76 | + _ <- TerminalOp.WriteLine(s"counter is $value").liftFree |
| 77 | + _ <- TerminalOp.WriteLine("command [+/-/0/exit]").liftFree |
| 78 | + input <- TerminalOp.ReadLine.liftFree |
| 79 | + _ <- input.trim.toLowerCase match { |
| 80 | + case "exit" => TerminalOp.WriteLine("See ya!").liftFree *> terminate |
| 81 | + case "+" => CounterOp.Increment.liftFree *> freeProgram |
| 82 | + case "-" => CounterOp.Decrement.liftFree *> freeProgram |
| 83 | + case "0" => CounterOp.Reset.liftFree *> freeProgram |
| 84 | + case _ => TerminalOp.WriteLine("Invalid command!").liftFree *> freeProgram |
| 85 | + } |
| 86 | + } yield () |
| 87 | + |
| 88 | + override val runc: IO[Unit] = |
| 89 | + freeProgram.foldMap(interpreter).run(0).map(_ => ()) |
| 90 | +} |
0 commit comments