Filter a F[List[Int]] using an Int => F[Boolean] 其中 F 是通用的

Filter a F[List[Int]] using an Int => F[Boolean] where F is generic

我正在尝试定义一个抽象代数,它允许我推迟选择我将用来包装有效操作(IO、任务、未来等)的 Monad,直到我 运行 程序。

trait MyAlg[F[_]]
  def isValid(v: int): F[Boolean]
  def getElements(): F[List[Int]]
  def filterValidElements(vs: F[List[Int]]): F[List[Int]]

假设我需要在 isValid 中做一些可能产生副作用的事情,比如检查数据库。

如果我可以离开 isValidgetElements 抽象 --- 这样例如一个实现可以连接到数据库而另一个实现可以引用模拟进行测试会更好 - -- 但定义一个通用的 filterValidElements 所以我不需要为这两种情况重新实现相同的功能。像这样:

def filterValidElements(es: F[List[Int]]]): F[List[Int]] = 
      es.map(
        elems => elems.map(
          e => (e, isValid(e))).collect{
            case (e, F(true)) => e
       })

但是,F 是通用的,因此它不提供 map 并且没有构造函数。

当然,我不能明确地将 F 设置为 Monad,例如

trait MyAlg[F: cats.Monad]

因为特征不能有带上下文边界的类型参数。

有没有什么方法可以编写我的 filterValidElements 函数,留下 isValid 抽象和 F 通用?

我对这种风格还很陌生,所以我可能会以完全错误的方式进行处理。

尝试添加隐式参数,指定您可以执行 map(即 Functor)和 pure 又名 point(即 InvariantMonoidal)。例如,您可以将其设置为 ApplicativeMonad.

import cats.{Functor, InvariantMonoidal}
import cats.syntax.functor._
import scala.language.higherKinds

trait MyAlg[F[_]] {
  def isValid(v: Int): F[Boolean]
  def getElements(): F[List[Int]]
  def filterValidElements(es: F[List[Int]])(implicit functor: Functor[F], im: InvariantMonoidal[F]): F[List[Int]] =
    es.map(
      elems => elems.map(
        e => (e, isValid(e))).collect{
          case (e, fb) if fb == im.point(true) => e
        })
}