Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EAFP in Haskell

I have a doubt of the Maybe and Either types, and their hypothetical relation to EAFP(Easier Ask Forgiveness to Permission). I've worked with Python and get used to work with the EAFP paradigm in the world of exceptions.

The classical example: Division by zero

def func(x,y):
    if not y:
        print "ERROR."
    else: return (x/y)

and Python's style:

def func(x,y):
    try:
        return (x/y)
    except: return None

In Haskell, the first function would be

func :: (Eq a, Fractional a) => a -> a -> a
func x y = if y==0 then error "ERROR." else x/y

and with Maybe:

func :: (Eq a, Fractional a) => a -> a -> Maybe a
func x y = if y==0 then Nothing else Just (x/y)

In Python's version, you run func without checking y. With Haskell, the story is the opposite: y is checked.

My question:

  1. Formally, does Haskell support the EAFP paradigm or "prefers" LBYL although admits a semi-bizarre EAFP approximation?

PD: I called "semi-bizarre" because, even if it is intuitively readable, it looks (at least for me) like it vulnerates EAFP.

like image 705
Carlos Alonso Gómez Avatar asked Jan 08 '23 13:01

Carlos Alonso Gómez


1 Answers

The Haskell style with Maybe and Either forces you to check for the error at some point, but it does not have to be right away. If you don't want to deal with the error now, you can just propagate it on through the rest of your computation.

Taking your hypothetical safe divide-by-0 example, you could use it in a broader computation without an explicit check:

do result <- func a b
   let x = result * 10
   return x

Here, you don't have to match on the Maybe returned by func: you just extract it into the result variable using do-notation, which automatically propagates failure throughout. The consequence is that you don't need to deal with the potential error immediately, but the final result of the computation is wrapped in Maybe itself.

This means that you can easily combine (compose) functions that miht result in an error without having to check the error at each step.

In a sense, this gives you the best of both worlds. You still only have to check for errors in one place, at the very end, but you're explicit about it. You have to use something like do-notation to take care of the actual propagation and you can't ignore the final error by accident: if you don't want to handle it, you have to turn it into a runtime error explicitly.

Isn't explicit better than implicit?

Now, Haskell also has a system of exceptions for working with runtime errors that you do not have to check at all. This is useful occasionally, but not too often. In Haskell, we only use it for errors that we do not expect to ever catch—truly exceptional situations. The rule of thumb is that a runtime exception represents a bug in your program, while an improper input or merely an uncommon case should be represented with Maybe or Either.

like image 52
Tikhon Jelvis Avatar answered Jan 19 '23 17:01

Tikhon Jelvis