Returning a boolean when success or failure is the sole concern

I often find myself returning a boolean from a method, that’s used in multiple locations, in order to contain all the logic around that method in a single place. All the (internal) calling method needs to know is whether the operation was successful, or not.

I’m using Python but the question isn’t necessarily specific to that language. There are only two options I can think of

  1. Raise an exception, though the circumstances are not exceptional, and remember to catch that exception in every place the function is called
  2. Return a boolean as I’m doing.

This is a really simple example that demonstrates what I’m talking about.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>import os
class DoSomething(object):
def remove_file(self, filename):
try:
os.remove(filename)
except OSError:
return False
return True
def process_file(self, filename):
do_something()
if remove_file(filename):
do_something_else()
</code>
<code>import os class DoSomething(object): def remove_file(self, filename): try: os.remove(filename) except OSError: return False return True def process_file(self, filename): do_something() if remove_file(filename): do_something_else() </code>
import os

class DoSomething(object):

    def remove_file(self, filename):

        try:
            os.remove(filename)
        except OSError:
            return False

        return True

    def process_file(self, filename):

        do_something()

        if remove_file(filename):
            do_something_else()

Although it’s functional, I really dislike this manner of doing something, it “smells”, and can sometimes result in a lot of nested ifs. But, I can’t think of a simpler way.

I could turn to a more LBYL philosophy and use os.path.exists(filename) prior to attempting deletion but there’s no guarantees the file won’t have been locked in the meantime (it’s unlikely but possible) and I still have to determine whether the deletion has been successful or not.

Is this an “acceptable” design and if not what would be a better way of designing this?

You should return boolean when the method/function is useful in making logical decisions.

You should throw an exception when the method/function isn’t likely to be used in logical decisions.

You have to make a decision about how important the failure is, and if it should be handled or not. If you could classify the failure as a warning, then return boolean. If the object enters a bad state that make future calls to it unstable, then throw an exception.

Another practice is to return objects instead of a result. If you call open, then it should return a File object or null if unable to open. This ensures the programmers has an object instance that is in a valid state that can be used.

EDIT:

Keep in mind that most languages will discard the result of a function when it’s type is boolean or integer. So it’s possible to call the function when there is no left hand assignment for the result. When working with boolean results, always assume the programmer is ignoring the returned value and use that to decide if it should rather be an exception.

2

A function is a contract, and its name should suggest what contract it will fulfil. IMHO, if you name it remove_file so it should remove the file and failing to do so should cause an exception. On the other hand, if you name it try_remove_file, it should “try” to remove and return boolean to tell if the file has been removed or not.

This would lead to another question — should it be remove_file or try_remove_file? It depends on your call site. Actually, you can have both methods and use them in different scenario, but I think removing file per-se has high chance of success so I prefer having only remove_file that throw exception when fail.

Your intuition on this is correct, there is a better way to do this: monads.

What Are Monads?

Monads are (to paraphrase Wikipedia) a way of chaining operations together while hiding the chaining mechanism; in your case the chaining mechanism is the nested ifs. Hide that and your code will smell much nicer.

There are a couple of monads that will do just that (“Maybe” and “Either”) and lucky for you they’re part of a really nice python monad library!

What they can do for your code

Here’s an example using the “Either” monad (“Failable” in the library linked to), where a function can return a Success or Failure, depending on what occurred:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>import os
class DoSomething(object):
def remove_file(self, filename):
try:
os.remove(filename)
return Success(None)
except OSError:
return Failure("There was an OS Error.")
@do(Failable)
def process_file(self, filename):
do_something()
yield remove_file(filename)
do_something_else()
mreturn(Success("All ok."))
</code>
<code>import os class DoSomething(object): def remove_file(self, filename): try: os.remove(filename) return Success(None) except OSError: return Failure("There was an OS Error.") @do(Failable) def process_file(self, filename): do_something() yield remove_file(filename) do_something_else() mreturn(Success("All ok.")) </code>
import os

class DoSomething(object):

    def remove_file(self, filename):
        try:
            os.remove(filename)
            return Success(None)
        except OSError:
            return Failure("There was an OS Error.")

    @do(Failable)
    def process_file(self, filename):
        do_something()
        yield remove_file(filename)
        do_something_else()
        mreturn(Success("All ok."))

Now, this might not look much different from what you have now, but consider how things would be if you had more operations that might result in a Failure:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code> def action_that_might_fail_and_returns_something(self):
# get some random value between 0 and 1 here
if value < 0.5:
return Success(value)
else:
return Failure("Bad value! Bad! Go to your room!")
@do(Failable)
def process_file(self, filename):
do_something()
yield remove_file(filename)
yield action_that_might_fail(somearg)
yield another_action_that_might_fail(someotherarg)
some_val = yield action_that_might_fail_and_returns_something()
yield something_that_used_the_return_value(some_val)
do_something_else()
mreturn(Success("All ok."))
</code>
<code> def action_that_might_fail_and_returns_something(self): # get some random value between 0 and 1 here if value < 0.5: return Success(value) else: return Failure("Bad value! Bad! Go to your room!") @do(Failable) def process_file(self, filename): do_something() yield remove_file(filename) yield action_that_might_fail(somearg) yield another_action_that_might_fail(someotherarg) some_val = yield action_that_might_fail_and_returns_something() yield something_that_used_the_return_value(some_val) do_something_else() mreturn(Success("All ok.")) </code>
    def action_that_might_fail_and_returns_something(self):
        # get some random value between 0 and 1 here
        if value < 0.5:
            return Success(value)
        else:
            return Failure("Bad value! Bad! Go to your room!")

    @do(Failable)
    def process_file(self, filename):
        do_something()
        yield remove_file(filename)
        yield action_that_might_fail(somearg)
        yield another_action_that_might_fail(someotherarg)
        some_val = yield action_that_might_fail_and_returns_something()
        yield something_that_used_the_return_value(some_val)
        do_something_else()
        mreturn(Success("All ok."))

At each of the yields in the process_file function, if the function call returns a Failure then the process_file function would exit out, at that point, returning the Failure value from the failed function, instead of continuing on through the rest and returning the Success("All ok.")

Now, imagine doing the above with nested ifs! (How would you handle the return value!?)

Conclusion

Monads are nice 🙂


Notes:

I’m not a Python programmer — I used the monad library linked to above in a script I ninja’d for some project automation. I gather, though, that in general the preferred, idiomatic approach is to use exceptions.

IIRC there’s a typo in the lib script on the page linked to though I forget where it is ATM. I’ll update if I remember. I diff’d my version against the page’s and found: def failable_monad_examle(): -> def failable_monad_example(): — the p in example was missing.

In order to get the result of a Failable decorated function (such as process_file) you have to capture the result in a variable and do a variable.value to get it.

In this particular case it may be useful to think about why you may not be able to remove the file. Let’s say the problem is that the file may or may not exist. Then you should have a function doesFileExist() that returns true or false, and a function removeFile() that just deletes the file.

In your code you would first check if the file exists. If it does, call removeFile. If not, then do other stuff.

In this case you may still want removeFile to throw an exception if the file cannot be removed for some other reason, such as permissions.

To summarize, exceptions should be thrown for things that are, well, exceptional. So if it is perfectly normal that the file you are trying to delete may not exist, then that is not an exception. Write a boolean predicate to check for that. On the other hand, if you do not have the write permissions for the file, or if it is on a remote file system that is suddenly inaccessible, those may very well be exceptional conditions.

2

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật