Let's say, I've the below code.
public int divide(int dividend, int divisor) {
if( divisor == 0 || (dividend == Integer.MIN_VALUE && divisor == -1))
throw new DivisionException();
return dividend/divisor;
}
How to write this in Functional Programming?
I have a logic similar to the above written in Java and would like to migrate that to functional code in Haskell/Clojure.
How to handle this in the callers of divide
?
I know that the above code is totally imperative. It was not written with the forethought of migrating it to FP in future.
Kindly advise me with a sample code in Haskell or Clojure.
Efficiency issues Functional programming languages are typically less efficient in their use of CPU and memory than imperative languages such as C and Pascal. This is related to the fact that some mutable data structures like arrays have a very straightforward implementation using present hardware.
In most functional programming languages, there is a type called Either (or a synonym). The Either type is used to represent a value that can have two possible types. It is common to see Either used to represent a success value or a failure value, although that doesn't have to be the case.
When dealing with errors in a purely functional way, we try as much as we can to avoid exceptions. Exceptions break referential transparency and lead to bugs when callers are unaware that they may happen until it's too late at runtime.
The divide
function is not total: some values from its input domain have no image.
Change the output domain so that it can return either an error or a number. The caller is responsible for checking whether the value is really a number, or an error.
In a dynamically typed language like Clojure, you could return nil
, but any other value could work too, as long as you can distinguish it from a number.
In a statically typed language like Haskell, use Data.Either
or your own datatype if you need.
The check is done consistently and statically in Haskell. You must do the check every time, even if you are sure the divisor can't be null. However, you can also have a wrapper function, must-divide
, which then would throws an exception on errors.
In Clojure, you may forget to check for nil
, either from a bug or because you have more information about the divisor than the compiler. You could however force a consistent checking by exporting a divide
macro that requires you to consider the error path:
(divide x y :on-error (throw ...))
(divide x y :on-error default-value)
... could be respectively expanded as:
(or (maybe-divide x y) (throw ...))
(or (maybe-divide x y) default-value)
... with
(defn maybe-divide [dividend divisor]
(and (not (zero? divisor))
(or (not= Integer/MIN_VALUE dividend)
(not= -1 divisor))
(/ dividend divisor)))
Mathematical operations are composed to form bigger expressions: adding an explicit error handling path inside them can quickly become unreadable.
Also, you may expect most of your operations to call divide
with valid inputs, and don't want to check if the result is valid each time you call it (e.g. some mathematical equation come with a proof that the divisor won't possibly ever be null). In that case, Clojure and Haskell support exceptions. This allows you to catch errors higher up in the call stack in case you have bugs.
The following shows how you could do it in Haskell.
Based on the type siginure divide :: Int -> Int -> Either [Char] Int
you can see that the function divide
will return either a Left string
or a Right Int
.
Either
is an algebraic data structure and there are many more and you can write your own to.
divide :: Int -> Int -> Either [Char] Int
divide dividend divisor
| (divisor == 0) = Left "Sorry, 0 is not allowed :o"
| (dividend == (minBound :: Int)) && (divisor == -1) = Left "somethig went wrong"
| otherwise = Right (dividend `div` divisor)
main = do
print (divide 4 2) -- Right 2
print (divide 4 0) -- Left "Sorry, 0 is not allowed :o"
print (divide (minBound :: Int) (-1)) -- Left "somethig went wrong"
You can play with it on repl.it
In Haskell you can throw errors to with error "and your error message"
but this will crash you programm.. and this is not what we want.
In Clojure this isn't really different from Java:
(defn divide
[dividend divisor]
(if (or (zero? divisor)
(and (= Integer/MIN_VALUE
dividend)
(= -1 divisor)))
(throw (DivisionException.))
(/ dividend divisor)))
Your code isn't mutating any variables and therefore is already pretty much functional. Exceptions are just as much a part of Clojure, because it adopts the execution model of the JVM.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With