问题描述
我有两条 use std::fs;
use std::str::Chars;
struct FileContents {
filename: String,contents: String,}
impl FileContents {
fn new(filename: String) -> Self {
let contents = fs::read_to_string(&filename).unwrap();
FileContents { filename,contents }
}
fn token_iter(&self) -> TokenIter<'_> {
TokenIter {
chars: self.contents.chars(),}
}
}
struct TokenIter<'a> {
chars: Chars<'a>,}
struct Token; // represents some chunk of chars
impl<'a> Iterator for TokenIter<'a> {
type Item = Token;
fn next(&mut self) -> Option<Self::Item> {
self.chars.next(); // call as many times as necessary to create token
Some(Token) // return created token here
}
}
fn example(filename: String) {
let file_contents = FileContents::new(filename);
let tokens = file_contents.token_iter();
for token in tokens {
// more processing here
}
}
路线:
Http4s
其中 val routes1:HttpRoutes[Task] = ???
val routes2:HttpRoutes[RTask] = ???
只是带有自定义环境的 RTask
:
Task/RIO
可以通过引入“zio-cats-interop”库并做正则的type RTask[A] = RIO[Env,A]
来完成两个相同类型参数的路由的组合,但是由于routes1<+>routes1
的类型参数是不变的,我可以'不要对不同的类型做同样的事情:
HttpRoutes
有什么解决办法吗?
解决方法
让我们处理一下事情:
type HttpRoutes[F[_]] = Http[OptionT[F,*],F]
type Http[F[_],G[_]] = Kleisli[F,Request[G],Response[G]]
所以您的 HttpRoutes[RTask]
实际上是 Kleisli[OptionT[RTask,Request[RTask],Response[RTask]]
。
假设您有 applyR: RTask ~> Task
(应用 R)和 requireR: Task ~> RTask
(添加不会使用的 R)。
val applyR: RTask ~> Task = new (RTask ~> Task) {
def apply[A](rtask: RTask[A]): Task[A] = ... // apply Env here
}
val requireR: Task ~> RTask = new (Task ~> RTask) {
def apply[A](task: Task[A]): RTask[A] = ... // require Env here
}
您可以调整 Kleisli 的每一个:输入、输出、使用它们的效果,尽管这将是一件苦差事:
val routesRTask: HttpRoutes[RTask]
val routesTask: HttpRoutes[Task] =
Kleisli { (reqTask: Request[Task]) =>
val reqRTask: Request[RTask] = req.mapK(requireR)
val responseRTask: OptionT[RTask,Response[RTask]] = routesRTask(reqTask)
responseRTask.mapK(applyR) // OptionT[Task,Response[RTask]]
.map(_.mapK(applyR)) // OptionT[Task,Response[Task]]
}
您可以将上述逻辑提取到某个函数中,并将其应用于所有应从 RTask
转换为 Task
的路由。