Home > Back-end >  For comprehension that has to handle 2 optional values and return a Option[T]
For comprehension that has to handle 2 optional values and return a Option[T]

Time:12-01

The below code works fine, but as you can see the 2nd clause in the for comprehension has a call that is unsafe.

case class ProductView(product: Product, stores: List[Store], warehosue: Option[Warehosue])

def loadView(...): ConnectionIO[Option[ProductView]] = 
for {
   product <- getProductById(id)  // ConnectionIO[Option[Product]]
   warehouse <- getWarehouse(product.get.warehouseId.get.id) // ConnectionIO[Option[Warehouse]]
   stores <- loadStores(...)     // ConnectionIO[List[Store]]
} yield product map { p =>
    ProductView(p, stores, warehouse)   
}

I tried to make that a safe call, but my types don't seem to line up.

   warehouse <- getWarehouse(product.get.warehouseId.get.id) 

How can I improve this, if any of the options is a None, I just want to return a None. This is suppose to return a Option[Warehouse]

I tried this:

warehouse <- product.map(p => p.warehouseId.map(id => getWarehouse(id)))

Hoping someone can help with this part of my for comprehension.

CodePudding user response:

The easiest way is using OptionT and flatTraverse

def loadView(id: Int):
  ConnectionIO[Option[ProductView]] =
    (for {
      product <- OptionT(getProductById(id))
      warehouse <- OptionT.liftF(product.warehouseId.flatTraverse(getWarehouse))
      stores <- OptionT.liftF(loadStores(...))
    } yield ProductView(product, stores, warehouse)).value

Also alternative variant without OptionT

  def loadView(id: Int): ConnectionIO[Option[ProductView]] = {
    getProductById(id).flatMap {
      _.traverse { product =>
        for {
          warehouse <- product. warehouseId.flatTraverse(getWarehouse)
          stores <- loadStores(...)
        } yield ProductView(product, stores, warehouse)
      }
    }
  }
  • Related