Hy there!
I recently started to use tapir. The server logic in there is an N tuple => Future[Either[E, A]]
function, where the N tuple
part is based on a builder (both the size of the tuple and the element types in the tuple).
I want to achieve an akka-http directive like server logic;
def authFunc(b: Boolean): Future[Either[String, Int]] =
if(b) Future.successful(1.asRight[String]) else Future.successful("error".asLeft[Int])
def appLogic(id: Int, name: String, s1: String, s2: String): Future[Either[String, Int]] =
Future.successful(1.asRight[String])
val endpointLogic: ((Boolean, String)) => Future[Either[String, Int]] = {
withF1(authFunc){ userId =>
unwrap0 { name =>
appLogic(userId, name, "", "")
}
}
}
val endpointLogic2: ((Boolean, String, String)) => Future[Either[String, Int]] = {
withFN(authFunc){ userId =>
unwrap1 { name: String =>
unwrap0 { email: String =>
appLogic(userId, name, email, "")
}
}
}
}
val endpointLogic3: ((Boolean, String, String, String)) => Future[Either[String, Int]] = {
withFN(authFunc){ userId: Int =>
unwrapN { (dummy: String) =>
unwrap1 { name: String =>
unwrap0 { email: String =>
appLogic(userId, name, email, dummy)
}
}
}
}
}
For this, I need something that can split a (A, B, C) =>
input into an A => (B, C) =>
input.
Both the unwraps and the withFs are easy to implement with shapeless:
def unwrap0[I, E, O](uf: I => Future[Either[E, O]]): I => Future[Either[E, O]] = uf
def unwrap1[I, K, E, O](uf: I => K => Future[Either[E, O]]): ((I, K)) => Future[Either[E, O]] = i => uf(i._1)(i._2)
def unwrapN[IN_TUPLE, OUT_TUPLE, I, E, O](uf: I => OUT_TUPLE => Future[Either[E, O]])
(implicit ic: IsComposite.Aux[IN_TUPLE, I, OUT_TUPLE])
: IN_TUPLE => Future[Either[E, O]] =
{inTuple =>
uf(ic.head(inTuple))(ic.tail(inTuple))
}
def withF1[I, K, E, A, O](af: I => Future[Either[E, A]])
(uf: A => K => Future[Either[E, O]])
(implicit ec: ExecutionContext)
: ((I, K)) => Future[Either[E, O]] =
{ i =>
af(i._1).flatMap {
case Left(e) => Future.successful(Left(e))
case Right(u) =>
uf(u)(i._2)
}
}
def withFN[IN_TUPLE, OUT_TUPLE, I, E, A, O](af: I => Future[Either[E, A]])
(uf: A => OUT_TUPLE => Future[Either[E, O]])
(implicit ic: IsComposite.Aux[IN_TUPLE, I, OUT_TUPLE], ec: ExecutionContext)
: IN_TUPLE => Future[Either[E, O]] =
{ inTuple =>
val t = ic.head(inTuple)
af(t).flatMap {
case Left(e) => Future.successful(Left(e))
case Right(u) =>
val outTuple = ic.tail(inTuple)
uf(u)(outTuple)
}
}
The problems:
- 0,1,N versions:
- the N version not working with non tuples
- the N version returns Tuple1[A] instead of A for the input (B, A)
- explicit types:
- the third example not working bcs the compiler can’t figure out the nested N calls
- bcs of the upper and lower function definitions it would be nice if all of the
: String
typedefs could be removed
I would be happy for any idea or advice in this matter, bcs my server logic functions are really ugly with the build in helper(s). (I know that I can use a case (a, b, c, d) => for{}yield()
but I think the hadoken style is more readable in this case, and I’m a bit mad that I can’t solve this )
Full gist: https://gist.github.com/tg44/c0e20c64714c60df2661eb1fcd73ab83