1

My understanding about pure functions are that the output of the function only depends on the input. And they are referentially transparent.

But then there are side effects that happens in our code. Below is an example

  import scala.concurrent.Future
  case class Customer(fName: String, lName: String)
  case class Order(orderId: Int, item: String, quantity: Int)
  case class Shipment(shipmentId: Int, orderId: Int, address: String)

  trait CustomerRepo {
    def findCustomer(userId: String): Future[Customer] = ???
    def findLastOrder(customer: Customer): Future[Order] = ???
    def findShipment(orderId: Int): Future[Shipment] = ???
  }

  class ShoppingApp(customerRepo: CustomerRepo) {

    def getLastShipment(userId: String): Future[Shipment] = {
      for {
        customer <- customerRepo.findCustomer(userId)
        lastOrder <- customerRepo.findLastOrder(customer)
        shipment <- customerRepo.findShipment(lastOrder.orderId)
      } yield shipment
    }

  }

Above is a basic example where we do multiple side effects (call to database).

getLastShipment returns a Future. Does that makes is a pure function.

If getLastShipment is not pure then how to make it pure???

abhishek
  • 31
  • 1
  • 6
  • There is nothing impure about `getLastShipment`. The only "problem" is that you're using `Future`, which is eagerly evaluated and breaks RT. Other than that, you're good. – Yuval Itzchakov Feb 15 '18 at 10:11
  • Futures work against the whole pure thing. Other than this I think to be pure the `getLastShipment` function should not depend on the context which means that it should explicitly require `CustomerRepo ` as a function parameter. – sarveshseri Feb 15 '18 at 10:19
  • Additionally: https://www.reddit.com/r/scala/comments/3zofjl/why_is_future_totally_unusable/ – Yuval Itzchakov Feb 15 '18 at 10:22

1 Answers1

1

You're pretty close, you're already on the right path using Monads and for-comprehensions, however, Future is fundamentally side-effecting, because it does not suspend its evaluation. (For further information, see my answer here. If you switch your function to use something like cats.effect.IO:

def getLastShipment(userId: String): IO[Shipment] = {
  for {
    customer <- customerRepo.findCustomer(userId)
    lastOrder <- customerRepo.findLastOrder(customer)
    shipment <- customerRepo.findShipment(lastOrder.orderId)
  } yield shipment
}

Then this code is completely pure and referentially transparent.

Luka Jacobowitz
  • 22,795
  • 5
  • 39
  • 57
  • Thanks @luka-jacobowit for your reply. What I understood from this is if we put a side-effect in a container (Options, Future - ok it is not perfect, Functors etc). This will make my method getLastShipment pure... I am just struggling to understand that how putting side-effect in a container make it pure.. Maybe can you point me to some material on that.... Thanks – abhishek Feb 15 '18 at 18:29
  • Sure @abhishek, maybe this article can help you https://underscore.io/blog/posts/2015/04/28/monadic-io-laziness-makes-you-free.html :) – Luka Jacobowitz Feb 16 '18 at 13:46