import cats._
import cats.data._
import cats.implicits._
import scala.concurrent._
import scala.concurrent.duration.Duration._
import ExecutionContext.Implicits.global
object Sk extends App {
val PLUS = ((_: Int) + (_: Int)).curried
trait Lam_[->>[_, _]] {
def app[E, A, B]: E ->> (A => B) => E ->> A => E ->> B
def fun[A, B]: (A => B) => A ->> (A => B)
def lam[A, B]: (A ->> A => A ->> B) => A ->> B
}
trait Lam[F[_]] extends Lam_[Kleisli[F, ?, ?]]
implicit def lama[F[_] : Applicative]: Lam[F] =
new Lam[F] {
def AP[E] = Applicative[Kleisli[F, E, ?]]
def app[E, A, B] = AP[E].ap[A, B]
def fun[A, B] = AP[A].pure[A => B]
def lam[A, B] = _ (Kleisli.ask[F, A])
}
object Lam {
def apply[F[_] : Lam] = implicitly[Lam[F]]
def app[F[_] : Lam, E, A, B](f: Kleisli[F, E, (A => B)])
(a: Kleisli[F, E, A]) =
Lam[F].app(f)(a)
def fun[F[_] : Lam, A, B](f: A => B) = Lam[F].fun(f)
def lam[F[_] : Lam, A, B](f: Kleisli[F, A, A] =>
Kleisli[F, A, B]) = Lam[F].lam(f)
}
import Lam._
def answer[F[_] : Lam]: Kleisli[F, Int, Int] =
lam(x => app(app(fun(PLUS))(x))(x))
def main[F[_] : Lam : Functor] = answer[F].map(println)
answer[Id].apply(21)
Await.ready(main[Future].apply(21), Inf)
}