yogupta
yogupta

Reputation: 328

Are functions with Try/Option signature a pure function?

I'm learning about functional programming and trying to understand pure functions.

Wikipedia says a function is pure if:

  1. No Side effects
  2. Maps input to output

Pure functions also satisfy referential transparency, do not access a global variable, and does not mutate input variables.

Are partial functions pure?

A function that has a signature type of

def foo(x: Int): Option[Int] = { if(x <= 0) None else Some(x) }
def bar(x: Int): Try[Int] = Try { require(x <= 0); x }

are these functions pure functions?

They don't map every input to proper output.

I know that declaring the data types as Option/Try lets the caller know that it might fail for certain inputs.

Are the above functions pure?

Upvotes: 0

Views: 170

Answers (1)

Mateusz Kubuszok
Mateusz Kubuszok

Reputation: 27595

Try and Option tell nothing whether side effects were used to create them.

def parseLong(str: String) = Try(str.toLong)

This is pure computation - it doesn't change global state, its output depends only on input. On the other hand:

Try {
  // create entry in database
}

would change the state of the outside world. Because Try and Future catch exceptions they are used in "Scala as better Java" as one ways to denote side effects, but you cannot enforce this or any other convention.

If you put side effects into Try { ... } they will be evaluated immediately and the result will be memoized, so you'll break referential transparency.

// tupled is from Cats
val t = Try{ println("foo"); 10 }

(t, t).tupled.map { case (a, b) => println(s"$a  $b") }

is not the same as

(Try{ println("foo"); 10 }, Try{ println("foo"); 10 }).tupled
  .map { case (a, b) => println(s"$a  $b") }

So whether or not function returning Try or Option or Future etc is pure, depends whether inside of them you perform side effects. This cannot be deduced from type at all. They might be pure or impure and you can only find out by looking inside. Whether they are partial or total also doesn't tell you anything.

In purely FP Scala you would be using some IO instead of Try or Future to handle code that might be async and/or failing and surely side effecting because it only compute values when it is asked to and every time you ask it to.

val t = IO { println("foo"); 10 }

(t, t).tupled.map { case (a, b) => println(s"$a $b") }

is the same as

(IO { println("foo"); 10 }, IO { println("foo"); 10 }).tupled
  .map { case (a, b) => println(s"$a $b") }

Then you'll use IO for all side effecting things, are you'll be very careful to never use anything else for impure computations (well, sometimes you might convert to/from Futures). So in such codebases functions returning Option/Either would almost always be pure. At least the parts that the programmers would directly control (libraries can have other conventions).

Upvotes: 1

Related Questions