Home > Enterprise >  better way to dissolve multiple Either arguments
better way to dissolve multiple Either arguments

Time:09-21

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?

CodePudding user response:

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.

  • Related