Snippet: Tagless Final vs OOP
These may as well be interview questions for Scala developers:
- Which signature do you prefer?
- Just looking at the signature, what can potentially be wrong with the “tagless final” version?
- Also discuss the 2 type-class approaches, what are the potential design problems there?
// -----------------
// Tagless final
def registerUser[F[_]: UserDB: EmailService: Monad](user: User): F[Unit]
// -----------------
// ReaderT (Kleisli) & OOP
def registerUser[F[_]: Monad](
user: User): Kleisli[F, (UserDB[F], EmailService[F]), Unit]
// -----------------
// Plain function parameters & OOP
def registerUser[F[_]: Monad](
db: UserDB[F],
es: EmailService[F],
user: User): F[Unit]
// -----------------
// OOP class
final class RegistrationService[F[_]: Monad](
db: UserDB[F],
es: EmailService[F]) {
def registerUser(user: User): F[Unit]
}
// -----------------
// OOP interface
trait RegistrationService[F[_]] {
def registerUser(user: User): F[Unit]
}
object RegistrationService {
def apply[F[_]: Monad](
db: UserDB[F],
es: EmailService[F]): RegistrationService[F] = ???
}
// -----------------
// Type Class (1) — two type params
trait RegistrationService[F[_], Env] {
def registerUser(env: Env, user: User): F[Unit]
}
object RegistrationService {
implicit def instance[F[_]: Monad]
: RegistrationService[F, (UserDB[F], EmailService[F])] = ???
}
// -----------------
// Type Class (2) — single type param
trait RegistrationService[Env[_[_]]] {
def registerUser[F[_]: Monad](env: Env[F], user: User): F[Unit]
}
object RegistrationService {
type Env[F[_]] = (UserDB[F], EmailService[F])
implicit val instance: RegistrationService[Env] = ???
}
| Written by Alexandru Nedelcu