Skip to content

svoisen/SVMaybe

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

20 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SVMaybe

A simple Maybe monad implementation in Objective-C.

Why Bother?

If you already know about Maybe from languages like Haskell, and you're familiar with Objective-C, you may be asking: "Why bother?" In Objective-C, sending a message to nil returns nil, so with a category on NSObject that adds a binding method like ifNotNil or Haskell's >>= operator, we already have Maybe monad functionality. Nothing more is necessary. But if we want more sophisticated binding functionality like that described below, this won't suffice. In that case, we need a "smarter" nil, which is what SVMaybe provides.

Overview

SVMaybe provides the following:

  1. An elegant solution to the common problem of nested nil checks in Objective-C code.
  2. Custom definitions of what is meant by "nothing" on a per-class basis ("semantic nil").

If you've ever found yourself writing something tedious like this:

NSDictionary *person = @{@"name":@"Homer Simpson", @"address":@{@"street":@"123 Fake St", @"city":@"Springfield"}};
NSString *cityString;

if (person)
{
    NSDictionary *address = (NSDictionary *)[person objectForKey:@"address"];
    if (address)
    {
        NSString *city = (NSString *)[address objectForKey:@"city"];
        if (city)
        {
            cityString = city;
        }
        else
        {
            cityString = @"No city."
        }
    }
    else
    {
        cityString = @"No address.";
    }
}
else
{
    cityString = @"No person.";
}

SVMaybe allows you to more concisely and declaratively provide the same solution:

NSDictionary *person = @{@"name":@"Homer Simpson", @"address":@{@"street":@"123 Fake St", @"city":@"Springfield"}};
NSString *cityString = [[[Maybe(person) whenNothing:Maybe(@"No person.") else:MapMaybe(person, [person objectForKey:@"address"])]
                                        whenNothing:Maybe(@"No address.")] else:MapMaybe(address, [address objectForKey:@"city"])]
                                        whenNothing:Maybe(@"No city.")] justValue];

It also allows you to move beyond simple nil checks by offering run-time redefinition of what is meant by "nothing" on a per-class basis. For instance, in the above example suppose that empty strings should also be considered "nothing." Here's the re-definition:

[NSString defineNothing:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
    return [(NSString *)evaluatedObject length] == 0;
}]];

Given this re-definition, if any of the strings in the above example were empty (or nil), the monad binding would short to nothing and return the appropriate default string wrapped in a SVMaybe.

Creating Maybes

Use the provided macros:

Maybe(@"foo");
Maybe(nil); // Equals Nothing
Nothing;

Or a static method:

[SVMaybe maybe:@"foo"];
[SVMaybe maybe:nil]; // Equals [SVMaybe nothing]
[SVMaybe nothing];

Getting Values

To get the value of a maybe:

[Maybe(@"foo") justValue] // "foo"
[Nothing justValue] // Throws an exception!

Binding and Chaining

SVMaybe offers a few other chaining options in addition to whenNothing:else described above:

  • andMaybe: Binds multiple maybe values together, returning the last bound maybe. If any maybe is "nothing," the binding shorts and returns "nothing." (Equivalent to >> in Haskell.)

  • whenSomething: Binds and maps multiple maybes together using the provided block. If any maybe is "nothing," the binding shorts and returns "nothing." (Equivalent to >>= in Haskell.)

  • whenNothing: Same as whenNothing:else: but without the else block. Returns self if not nothing.

About

A Maybe monad implementation for Objective-C

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published