使用Int => F [Boolean]过滤F [List [Int]],其中F是通用的

时间:2018-09-05 12:34:55

标签: scala generic-programming scala-cats

我正在尝试定义一个抽象代数,使我可以推迟选择要使用的Monad来包装有效的操作(IO,Task,Future等),直到运行程序为止。

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是泛型的?

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

1 个答案:

答案 0 :(得分:2)

尝试添加隐式参数,以指定您可以执行map(即Functor)和pure aka 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
        })
}