I have a model, that looks like this:
case class ItemEntity private(
userId: IdValue,
title: TitleValue,
description: DescriptionValue,
media: MediaValue,
price: MoneyValue,
stock: StockValue
)
And in the companion object of this model, is a constructor, that currently looks like this:
object ItemEntity
{
def fromUnsafe(
mayBeUserId: Either[IdError, IdValue],
mayBeTitle: Either[TitleError, TitleValue],
mayBeDescription: Either[DescriptionError, DescriptionValue],
mayBeMedia: Either[MediaError, MediaValue],
mayBePrice: Either[MoneyError, MoneyValue],
mayBeStock: Either[StockError, StockValue]
): Either[ItemError, ItemEntity] =
{
mayBeUserId match
case Right(id) => mayBeTitle match
case Right(title) => mayBeDescription match
case Right(description) => mayBeMedia match
case Right(media) => mayBePrice match
case Right(price) => mayBeStock match
case Right(stock) => Right(
ItemEntity(
userId = id,
title = title,
description = description,
media = media,
price = price,
stock = stock
)
)
case Left(stockError) => Left(ItemError.Error(stockError))
case Left(priceError) => Left(ItemError.Error(priceError))
case Left(mediaError) => Left(ItemError.Error(mediaError))
case Left(descriptionError) => Left(ItemError.Error(descriptionError))
case Left(titleError) => Left(ItemError.Error(titleError))
case Left(idError) => Left(ItemError.Error(idError))
}
}
As you see, the code has a lot of boilerplate.
But I cannot think of a better way to wirte this constructor...
So my question is:
Is there a better way, to write this?
You can use for-comprehension but first you will have to map or widen your left value to the same type:
def fromUnsafe(
mayBeUserId: Either[IdError, IdValue],
mayBeTitle: Either[TitleError, TitleValue],
mayBeDescription: Either[DescriptionError, DescriptionValue],
mayBeMedia: Either[MediaError, MediaValue],
mayBePrice: Either[MoneyError, MoneyValue],
mayBeStock: Either[StockError, StockValue]
): Either[ItemError, ItemEntity] =
for {
id <- mayBeUserId.left.map(ItemError.Error)
title <- mayBeTitle.left.map(ItemError.Error)
description <- mayBeDescription.left.map(ItemError.Error)
media <- mayBeMedia.left.map(ItemError.Error)
price <- mayBePrice.left.map(ItemError.Error)
stock <- mayBeStock.left.map(ItemError.Error)
} yield ItemEntity(id, title, description, media, price, stock)
If the error types can be widen to the same type then you can also move the left.map
after the for-comprehension.
(for {
id <- mayBeUserId
title <- mayBeTitle
description <- mayBeDescription
media <- mayBeMedia
price <- mayBePrice
stock <- mayBeStock
} yield ItemEntity(id, title, description, media, price, stock)).left.map(ItemError.Error)
You can also replace left.map
with leftMap
and/or leftWiden
with Cats.