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 SK[F[_]] {
type ->>[A, B] = Kleisli[F, A, B]
def S[E, A, B]: E ->> (A => B) =>
E ->> A => E ->> B
def K[E, A]: (E => A) => E ->> (E => A)
def I[A]: A ->> A
}
object SK {
def apply[F[_] : SK] = implicitly[SK[F]]
def S[F[_] : SK, E, A, B](f: SK[F]# ->>[E, A => B])
(a: SK[F]# ->>[E, A]) =
SK[F].S(f)(a)
def K[F[_] : SK, E, A](f: E => A) = SK[F].K(f)
def I[F[_] : SK, A] = SK[F].I[A]
}
implicit def ska[F[_] : Applicative]: SK[F] =
new SK[F] {
def AP[E] = Applicative[E ->> ?]
def S[E, A, B] = AP[E].ap[A, B]
def K[E, A] = AP[E].pure[E => A]
def I[A] = Kleisli.ask[F, A]
}
import SK._
def answer[F[_] : SK] = S(S(K(PLUS))(I))(I)
def main[F[_] : SK : Functor] =
answer[F].map(println)
main[Id].apply(21)
Await.ready(main[Future].apply(21), Inf)
}