Я пытаюсь определить абстрактную алгебру, которая позволит мне отложить выбор того, какую монаду я буду использовать, чтобы обернуть эффективную операцию (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
и не имеет конструктора.
Конечно, я не могу установить F
в качестве монады, например
trait MyAlg[F: cats.Monad]
потому что у черт не может быть параметров типа с границами контекста.
Можно ли написать мою filterValidElements
функцию, оставив isValid
abstract и F
generic?
Я довольно новичок в этом стиле, поэтому, возможно, я поступаю по этому поводу совершенно неправильно.