A lot of the time, when we discuss the effect, we usually talk about side-effect. However, as I study more and more into functional programming and reading more and more functional programming books, I noticed many times “Effect” or “Effectful” had been widely said in the FP community when describing abstract things.
I dig a little deeper into what an “Effect” or “Effectful” means and put that in this blog post for a note to my future self.
It is not Side Effect
Usually, what they meant for “Effect” or “Effectful” is no side effect (sometimes it does). It is Main Effect.
It has something to do with Type Category
A type category is a Math Structure to abstract out representation for all the different fields in Math. When designing a program, we can think in the properties of that program before writing code instead of the other way around. For example, a function sum
can be empty (identity law), has the property of combined operation and needs to be associative. (1+2 is equal to 2+1). We can characterize them as Monoid, and restrict input function to be a Monoid. This way, we can create a solution in a systematic approach that generates fewer bugs.
Within Type Category, there is Monad, which is a fancy word for a wrapper that produces an “effect” on a given type. I will quote the statement that Alvin Alexander mentioned in Functional and Reactive Domain Modeling:
- Option models the effects of optionality
- Future models latency as an effect
- Try abstract the consequences of failures
Those statements can be rewritten as:
- Option is a monad that models the effect of optionality (of being something optional)
- Future is a monad that models the impact of latency
- Try is a monad that models the impact of failures (manages exception as an effect)
Similarly:
- Reader is a monad that models the effect of composting operations based on some input.
- Writer is a monad that models the impact of logging
- State is a monad that models the impact of State
- Sync in Cats-effect is a monad that models the effects of synchronous lazy execution.
It is an F[A] instead of A
An effect can be said of what the monad handles.
Quoting from Rob Norris in Functional Programming with Effects - an effectual function returns F[A]
rather than A
.
For example:
def division(num1:Int, num2:Int): Either[Exception, Int] = if(num2 == 0) Left(new IllegalStatement("num1 cannot divide by zero")) else Right(num1/num2)
By looking at the code above, we know that without diving through the code, the function can have an “effect” of returning some Exception or integers based on the input type. By explicitly declaring what effect your function can return, other tasks that call division
will need to supply a mechanism to handle those “effect” types, making your program deterministic.
"I don’t remember where I read it, but someone said that if you want to think about it philosophically, when a function returns an A, that A has already been fully evaluated; but if that function returns F[A] instead, that result has not already been fully evaluated, the A is still inside F[A] waiting to be evaluated. So, rather than writing a function that returns a raw type, an effectful function returns a raw type inside a useful wrapper — where that wrapper is a monad that lets the result be used in a monadic style (i.e., in a Scala for-expression)."
Summary
- Effect can also mean the “main-effect” of your program.
- Instead of thinking about Side-effect, and effect is what the monad handles.
- An “effectful” function returns a
F[A]
rather thanA