pauljamescleary/scala-pet-store

Remove monad transformer return types from algebras

Ghurtchu opened this issue · 1 comments

Monad transformer return types such as EitherT or OptionT in algebra definitions is a kind of an anti-pattern, it is better to have F[Option[A]] or F[Either[A, B]] there.

If you would like to see the alternative approach then I can open a small PR which demonstrates the positive changes but I think I will need some kind of access to do that, thank you.

alternative algebra:

trait PetValidationAlgebra[F[_]] {
 
 /* Fails with a PetAlreadyExistsError */
  def doesNotExist(pet: Pet): F[Either[PetAlreadyExistsError, Unit]]

  /* Fails with a PetNotFoundError if the pet id does not exist or if it is none */
  def exists(petId: Option[Long]): F[Either[PetNotFoundError.type, Unit]]

}

alternative interpreter:

package io.github.pauljamescleary.petstore.domain
package pets

import cats.Applicative
import cats.syntax.functor._
import cats.syntax.either._
import cats.syntax.applicative._

class PetValidationInterpreter[F[_]: Applicative](repository: PetRepositoryAlgebra[F])
    extends PetValidationAlgebra[F] {

  override def doesNotExist(pet: Pet): F[Either[PetAlreadyExistsError, Unit]] =
    repository.findByNameAndCategory(pet.name, pet.category).map { pets =>
      val petNames = pets.map(_.bio)

      Either.cond(!petNames.contains(pet.name), (), PetAlreadyExistsError(pet))
    }

  override def exists(petId: Option[Long]): F[Either[PetNotFoundError.type, Unit]] = {
    petId.fold(PetNotFoundError.asLeft[Unit].pure[F]) { id =>
      repository.get(id).map { maybePet =>
        Either.cond(maybePet.isDefined, (), PetNotFoundError)
      }
    }
  }
}

object PetValidationInterpreter {
  def apply[F[_]: Applicative](repository: PetRepositoryAlgebra[F]) =
    new PetValidationInterpreter[F](repository)
}