/***
coursier.CoursierPlugin.projectSettings
scalaVersion := "2.11.8"

libraryDependencies += "org.typelevel" %% "cats" % "0.7.0"

resolvers += Resolver.sonatypeRepo("releases")

addCompilerPlugin("org.spire-math" %% "kind-projector" % "0.9.0")

// if your project uses multiple Scala versions, use this for cross building
addCompilerPlugin("org.spire-math" % "kind-projector" % "0.9.0" cross CrossVersion.binary)

// if your project uses both 2.10 and polymorphic lambdas
libraryDependencies ++= (scalaBinaryVersion.value match {
  case "2.10" =>
    compilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full) :: Nil
  case _ =>
    Nil
})

*/


import cats._
import cats.instances.all._
import cats.syntax.flatMap._
import cats.free.Free
import scala.concurrent.{ Await, Future }
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.language.higherKinds

object Main extends App {

  case class Tweet(v: String)

  class Decoder {
    def decode(t: List[String]): List[Tweet] =
      t.map(Tweet(_))
  }

  sealed trait TweetOp[A]
  object TweetOp {
    case class GetTweets[A](userId: String) extends TweetOp[List[Tweet]]

    def getTweets(userId: String): Free[TweetOp, List[Tweet]] =
      Free.liftF[TweetOp, List[Tweet]](GetTweets(userId))
  }

  sealed trait HttpOp[A]
  object HttpOp {
    case class Get[A](url: String, decoder: Decoder) extends HttpOp[List[Tweet]]

    def get[A](url: String, decoder: Decoder): Free[HttpOp, List[Tweet]] =
      Free.liftF[HttpOp, List[Tweet]](Get[A](url, decoder))
  }

  sealed trait LogOp[A]
  object LogOp {
    case class Log[A](message: String) extends LogOp[Unit]

    def log[A](message: String): Free[LogOp, Unit] =
      Free.liftF[LogOp, Unit](Log[A](message))
  }

  type Halt[F[_], A] = F[Unit]

  import HttpOp._
  import TweetOp._
  import LogOp._

  val tweetToLog: (TweetOp ~> Free[LogOp, ?]) = new (TweetOp ~> Free[LogOp, ?]) {
    def apply[A](fa: TweetOp[A]): Free[LogOp, A] = fa match {
      case GetTweets(userId) =>
        LogOp.log("TweetOp GetTweets.log message").asInstanceOf[Free[LogOp, A]]
    }
  }

  val tweetToHttp: (TweetOp ~> Free[HttpOp, ?]) = new (TweetOp ~> Free[HttpOp, ?]) {
    def apply[A](fa: TweetOp[A]): Free[HttpOp, A] = fa match {
      case GetTweets(userId) =>
        HttpOp.get(s"user/$userId/tweets", new Decoder()) // operation from differetn interperter

    }
  }

  val httpToFuture: HttpOp ~> Future = new (HttpOp ~> Future) {
    def apply[A](fa: HttpOp[A]): Future[A] = fa match {
      case Get(url, decoder) =>
        Future {
          val httpResp = List(s"tweet of userId for $url")
          decoder.decode(httpResp)
        }
    }
  }

  val program: Free[TweetOp, List[Tweet]] =
    for {
      t <- getTweets("userId")
    } yield t

  // App flow TweetOp ~> HttpOp ~> Future

  val tweetToFuture: TweetOp ~> Future =
    λ[TweetOp ~> Future](x => tweetToHttp(x).foldMap(httpToFuture))

  val future: Future[List[Tweet]] = program.foldMap(tweetToFuture)

  println(Await.result(future, 10.second))
}
        
  
Processing...
Reused last reload result
[info] Loading project definition from /tmp/rendererXRBiRdJeHi/project/project
[info] Loading project definition from /tmp/rendererXRBiRdJeHi/project
[warn] Discarding 33 session settings.  Use 'session save' to persist session settings.
[info] Set current project to rendererWorker (in build file:/tmp/rendererXRBiRdJeHi/)
[info] Reapplying settings...
[info] Set current project to rendererWorker (in build file:/tmp/rendererXRBiRdJeHi/)
List(Tweet(tweet of userId for user/userId/tweets))
[info] Formatting 1 Scala source {file:/tmp/rendererXRBiRdJeHi/}rendererWorker(compile) ...
[info] Reformatted 1 Scala source {file:/tmp/rendererXRBiRdJeHi/}rendererWorker(compile).
[info] Compiling 1 Scala source to /tmp/rendererXRBiRdJeHi/target/classes...
[success] Total time: 8 s, completed Oct 16, 2016 1:04:48 PM
Now running...
[info] Running Main 
List(Tweet(tweet of userId for user/userId/tweets))
[success] Total time: 1 s, completed Oct 16, 2016 1:04:49 PM