Фильтруйте F[List[Int]], используя Int => F[Boolean], где F - универсальный
Я пытаюсь определить абстрактную алгебру, которая позволит мне отложить выбор того, какую монаду я буду использовать для переноса эффективной операции (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
вроде проверить дб.
Что было бы хорошо, если бы я мог уйти isValid
а также getElements
абстрактный --- так что, например, одна реализация может подключаться к базе данных, а другая может ссылаться на макет для тестирования ---, но определить общий 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
и не имеет конструктора.
Конечно, я не могу установить Explicity F
быть монадой, например как
trait MyAlg[F: cats.Monad]
потому что черты не могут иметь параметры типа с контекстными границами.
Есть ли способ написать мой filterValidElements
функция, оставляя isValid
абстрактный и F
общий?
Я довольно новичок в этом стиле, так что, возможно, я поступаю совершенно неправильно.
1 ответ
Попробуйте добавить неявные параметры, указывающие, что вы можете сделать map
(т.е. Functor
) а также pure
ака point
(т.е. InvariantMonoidal
). Например, вы можете сделать это Applicative
или же Monad
,
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
})
}