NAME

Sq::Core::Option - Option functions

This module is automatically loaded and accessible under Option by loading Sq.

DESCRIPTION

An Option is an alternative to represent undef/null values. By default every value in Perl can be undef. The problem with working with undef is that you need to check them but are never forced todo so. If you forget to check for undef than typical warnings in Perl will appear or maybe even the program crashes when you try to call a method on an undef value.

An option is a way to make it explicitly visible that a function sometimes returns no value at all. A user of the option value must explicitly work with an option and unpack the value or use some of the methods provided by the option module instead of directly extracting the value. When a value is extracted the user must write code for both cases that reduces the amount of errorness code that is possible to write.

But the whole idea on top of that is that there are special functions written that helps working and doing typical task with optional values.

Like the other modules you always can call every method in a functional-style.

By loading Sq it automatically imports the function Some and None to create optional values.

my $x = Some(10);
my $y = Some(0);
my $z = None;

CONSTRUCTORS

These function creates optional values.

Some(@xs) : $opt_xs

The Some function is automatically imported by loading Sq. Some can take multiple arguments and is valid as long every value is defined and not a None. All optionals are flattened on creation and thus it is like working with Arrays in list context. Every value in Some must be valid, otherwise the whole turns to None.

my $opt = Some();                         # None
my $opt = Some(undef);                    # None
my $opt = Some(10);                       # Some(10)
my $opt = Some([]);                       # Some([])
my $opt = Some({ a => 1, b => 2});        # Some({ a => 1, b => 2})
my $opt = Some(Some(10));                 # Some(10)
my $opt = Some(None);                     # None
my $opt = Some(Some(Some([])));           # Some([])
my $opt = Some(Some(1,2), Some(3,4));     # Some(1,2,3,4)
my $opt = Some(Some(1,2), undef);         # None
my $opt = Some(Some(1,2), Some(Some(3))); # Some(1,2,3)
my $opt = Some(1,2,None);                 # None

None() : $opt

This indicates the absence of a value

my $opt = None();
my $opt = None;

METHODS

Methods work on an optional value. All Methods can also be called in a functional-style. This means you always can do both calling styles.

$opt->map(sub($x) { ... });
Option::map($opt, sub($x) { ... });

is_some($any) : $bool

returns a truish value when the optional contains some value. Most of the time you probably want to use an extraction method instead.

if ( $opt->is_some ) {
    ...
}

Calling this method in a functional-style has the benefit that it also does a proper type-checking of an optional, you can pass any value to it and get back if the value is an optional and if it is Some value.

my $bool = Option::is_some($any)

my $bool = Option::is_some(Some({}));    # 1
my $bool = Option::is_some(Some(1));     # 1
my $bool = Option::is_some(None);        # 0
my $bool = Option::is_some(Some(undef)); # 0
my $bool = Option::is_some("");          # 0
my $bool = Option::is_some(0);           # 0
my $bool = Option::is_some([]);          # 0
my $bool = Option::is_some({});          # 0

is_none($any) : $bool

returns a truish value when the optional is None. Most of the time you probably want to use an extraction method instead.

if ( $opt->is_none ) {
    ...
}

Calling this method in a functional-style has the benefit that it also does a proper type-checking of an optional, you can pass any value to it and get back if the value is an optional and if it is a None value.

my $bool = Option::is_none(Some({}));    # 0
my $bool = Option::is_none(Some(1));     # 0
my $bool = Option::is_none(None);        # 1
my $bool = Option::is_none(Some(undef)); # 1
my $bool = Option::is_none("");          # 0
my $bool = Option::is_none(0);           # 0
my $bool = Option::is_none([]);          # 0
my $bool = Option::is_none({});          # 0

Consider that other values than Some or None also return 0 because an empty string for example is not an optional.

match($opt, Some => sub($value){ $y }, None => sub(){ $y }) : $y

Pattern matches against the optional value. It either runs the function provided with the Some argument or the None argument. It's usually used to extract the value by providing two functions for each case the optional can be in. If you just want to extract the value or use a default value then use the method or instead.

my $number =
    $opt->match(
        Some => sub($x) { $x + 1 },
        None => sub     { 0      },
    );

# will be 11
my $number =
    Some(10)->match(
        Some => sub($x) { $x + 1 },
        None => sub     { 0      },
    );

# will be 0
my $number =
    None->match(
        Some => sub($x) { $x + 1 },
        None => sub     { 0      },
    );

or($opt, @defaults) : $x

Returns the value when it is Some value or the @defaults value passed to it when it is None.

my $a  = $opt    ->or(10);
my $b  = Some(10)->or(0);      # 10
my $c  = None    ->or(0);      #  0
my $d  = Option::or($opt, 10);
my $e  = Some(1,2)->or(3);     # 1
my @xs = None     ->or(1,2,3)  # (1,2,3)

or_else($opt, $default_opt) : $opt

Similar to or but returns $opt as-is as long it is Some value. Otherwise returns $default_opt that is expected to be an optional.

my $opt = $opt   ->or_else(Some("foo"));
my $opt = Some(1)->or_else(Some 2);      # Some 1
my $opt = None   ->or_else(Some 3);      # Some 3

or_with($opt, $f_x) : $x

Same as or but instead of passing a value that is used in the None case, a function is expected and executed. This is useful in two cases.

  • When the or case should create different mutable values. Also when those values only should be lazily created only when the value is None. Consider that when using or you always already create a value before the or function is run. For just "normal" values like strings or numbers this might be okay. But when the $default value might execute some kind of side-effect or has some expensive cost attached to it, you highly want to use or_with.

  • When you want to execute a piece of code containing some kind of side-effects that only should be executed when the value was None.

# always returns a new array-ref
my $x = $opt->or_with(sub { [] });

# returns current time in the case of None
my $x = $opt->or_with(sub { scalar localtime });

or_else_with($opt, $f_opt) : $opt

When $opt is Some value returns $opt as-is. Otherwise executes and returns the result of $f_opt->() that should generate an optional value.

my $opt = Some([1])->or_else_with(sub { Some [] }) # Some [1]
my $opt = None     ->or_else_with(sub { Some [] }) # Some []

map($opt, $f_x) : $opt_x

When you have a function that don't know about optionals then you use map to let the function run of its value. When the optional is None then the function will not be executed, otherwise the function is executed with the value that the optional holds. Always returns an optional.

map is good for functions with the signature 'a -> 'b

# Here $incr is a function expecting a number and returning a number
# no optional is involved. It's basically of type: number -> number
my $incr = sub($x) { $x + 1 };

my $opt = $opt    ->map($incr);
my $opt = Some(0) ->map($incr); # Some(1)
my $opt = Some(10)->map($incr); # Some(11)
my $opt = None    ->map($incr); # None

map2($optA, $optB, $f_x) : $opt_x

Whenever you have a function expecting two normal values, but you have two optional values, you use this function. Returns an optional value again.

my $add = sub($x,$y) { $x + $y }

# adds together both optional values or returns None if one of them is None
my $opt = Option::map2($optA, $optB, $add);
my $opt = $optA->map2($optB, $add);

my $opt = Some(10)->map2(Some(3), $add); # Some(13)
my $opt = None    ->map2(Some(3), $add); # None

map3($optA, $optB, $optC, $f_x) : $opt_x

Same as map2 but for three optional values.

# adds all three optional together as long all of them are Some value
# otherwise $x will be None.
my $opt =
    Option::map3($optA, $optB, $optC, sub($a, $b, $c) {
        return $a + $b + $c;
    });

map4($optA, $optB, $optC, $optD, $f_x) : $opt_x

Same as map3 but for four optional values.

# adds all four optional together as long all of them are Some value
# otherwise $x will be None.
my $opt =
    Option::map4($optA, $optB, $optC, $optD, sub($a, $b, $c, $d) {
        return $a + $b + $c + $d;
    });

map_v(@opts, $f_x) : $opt_x

Expects a variable amount of optional values and a function as the last argument. Executes the function with all unpacked values when all optionals are Some values. Otherwise returns None.

Theoretically map_v can replace all other mapX functions, but consider that this function is usually slower because of its implementation compared to directly calling map, map2, map3 or map4.

# Some(28)
my $opt = Option::map_v(Some 1, Some 2, Some 3, Some 4, Some 5, Some 6, Some 7, sub {
    my $sum = 0;
    for my $x ( @_ ) {
        $sum += $x;
    }
    return $sum;
});

bind($opt, $f_opt) : $opt

bind is also sometimes called chain or then (depending on language or module). It allows chaining together functions that expects a non optional value, but returns an optional value again.

# this function parses a string and returns an optional. Either Some integer
# when the functions succeeds or None if not.
# Type: string -> Option<int>
my $parse_int = sub($str) {
    if ( $str =~ m/\A \s* (\d+) \s* \z/xms ) {
        return Some($1);
    }
    return None;
};

# The result will be Option<int>. Some(int) or None.
my $opt = $opt->bind($parse_int);

my $opt = Some("10")->bind($parse_int); # Some(10)
my $opt = Some("ab")->bind($parse_int); # None
my $opt = None      ->bind($parse_int); # None

# compare it when you have used map instead of bind
my $opt = Some("10")->map($parse_int);  # Some(Some(10))
my $opt = Some("ab")->map($parse_int);  # Some(None)
my $opt = None      ->map($parse_int);  # None

bind2($optA, $optB, $f_opt) : $opt

When you have two optional values to unpack and want them to pass it to a function that produces a new optional value.

# adds $optA and $optB together when both values are C<Some> value and
# greater than 0. returns Some sum or None.
my $opt_sum =
    Option::bind2($optA, $optB, sub($a, $b) {
        if ( $a > 0 && $b > 0 ) {
            return Some($a + $b);
        }
        return None;
    });

my $opt_sum =
    $optA->bind2($optB, sub($a, $b) {
        if ( $a > 0 && $b > 0 ) {
            return Some($a + $b);
        }
        return None;
    });

bind3($optA, $optB, $optC, $f_opt) : $opt

Unpacks three optional values and when all three are Some values passes them to $f_opt that returns an optional value.

my $opt_sum =
    Option::bind3($optA, $optB, $optC, sub($a,$b,$c) {
        if ( $a > 0 && $b > 0 && $c > 0 ) {
            return Some($a + $b + $c);
        }
        return None;
    });

bind4($optA, $optB, $optC, $optD, $f_opt) : $opt

Unpacks four optional values and when all three are Some values passes them to $f_opt that returns an optional value.

my $opt_sum =
    Option::bind4($optA, $optB, $optC, $optD, sub($a,$b,$c,$d) {
        if ( $a > 0 && $b > 0 && $c > 0 && $d > 0 ) {
            return Some($a + $b + $c + $d);
        }
        return None;
    });

bind_v(@opts, $f_opt) : $opt

Unpacks all optionals and when all values are Some value then passes all values to function $f_opt that returns an Optional value again.

my $sum_under_100 = sub {
    my $sum = 0;
    for my $x ( @_ ) {
        $sum += $x;
    }
    return $sum <= 100
         ? Some($sum)
         : None;
};

my $opt = Option::bind_v(Some 1, Some 2, Some 3, Some 4, Some 5, Some 6, $sum_under_100); # Some(21)
my $opt = Option::bind_v(Some 30, Some 50, Some 40,                      $sum_under_100); # None

validate($opt, $predicate) : $opt

Runs a predicate function (a function returning a boolish value) on the value. When $opt is Some value and the $predicate function returns a truish value then $opt will not change. Otherwise returns None instead.

my $is_normalized = sub($x) {
    return $x >= 0 && $x <= 1.0 ? 1 : 0;
};

my $opt = Some(0)  ->validate($is_normalized) # Some(0)
my $opt = Some(0.2)->validate($is_normalized) # Some(0.2)
my $opt = Some(2)  ->validate($is_normalized) # None
my $opt = None     ->validate($is_normalized) # None

check($opt, $predicate) : $bool

Similar to validate. But instead of returning an optional that contains the information if value was valid or not it directly returns the boolean value. A None value always return a falsish value.

my $bool = Some(0)      ->check(\&is_num) # 1
my $bool = Some(1)      ->check(\&is_num) # 1
my $bool = Some("0E0")  ->check(\&is_num) # 1
my $bool = Some(" 100") ->check(\&is_num) # 1
my $bool = Some("0.00") ->check(\&is_num) # 1
my $bool = Some("+0.00")->check(\&is_num) # 1
my $bool = Some("f100") ->check(\&is_num) # 0
my $bool = Some(undef)  ->check(\&is_num) # 0
my $bool = Some("")     ->check(\&is_num) # 0
my $bool = None         ->check(\&is_num) # 0

fold($opt, $state, $f_state) : $state

When you have a function expecting two normal values, but one value is an optional, then this function is probably what you wanna use. It either returns $state when the optional is None. Otherwise it executes $f_state by passing it the value of the $opt and the $state.

This function either returns $state or whatever $f_state returns. The type of $state and whatever $f_state returns should be the same, oterwise this function hardly makes any sense.

my $add = sub($x, $state) { $x + $state };

my $x = $opt    ->fold(100, $add);
my $x = Some(10)->fold(100, $add); # $add->(10,100) -> 110
my $x = None    ->fold(100, $add); # $state         -> 100

my $x = Option::fold(Some(10), 3, sub($x,$y) { $x - $y }); # 10 - 3 = 7

fold_back($opt, $state, $f_state) : $state

Same as fold but $state is passed as the first argument to $f_state instead of last. This behaviour can be more useful when Some contains multiple values.

my $sum = Some(1,2,3)->fold_back(10, sub($state,@rest) {
    # 10   + (1+2+3)
    $state + Array::sum(\@rest)
});

iter($opt, $f) : void

Runs function $f when there is Some value otherwise does nothing when the value is None. Usually this is done for side-effects as the funtion returns nothing.

Some("Hello")->iter(sub($str) { say $str }); # prints: "hello"
None         ->iter(sub($str) { say $str }); # prints nothing

to_array($opt) : $array_x

Converts the optional into an array. In the case of Some value it turns into an array of length 1 containing the value, otherwise it is an empty array. Arrays are blessed into the Sq Array.

my $array = $opt->to_array();
my $array = Some(1)->to_array; # [1]
my $array = None   ->to_array; # []

single($opt) : $opt_array

An optional can contain multiple values, but all of those values are usually also passed as multiple values to the lambda function or are returned. It is sometimes better to work with an array instead.

This function transforms the inner values into an Sq Array. This transformation also happens for a single value. When the option only has a single value that already is an array, than nothing happens. So it safe to call multiple times single.

single does not return a new Array or a copy when it is what it should be. It just returns $opt as-is. Also adds Array blessing when a normal array was passed.

my $array = Some(1,2,3)->single->get;                        # [1,2,3]
my $array = Some(1,2,3)->single->or(sq []);                  # [1,2,3]
my $sum   = Some(1,2,3)->single->map(call 'sum');            # Some(6)
my $sum   = Some(1,2,3)->map(sub(@xs) { Array::sum(\@xs) }); # 6

get($opt) : $x | EXCEPTION

Returns the value inside of an optional, but only when the value is Some value, otherwise throws an exception. This function should be avoided as much as possible if you want a working program. At least you should use is_some before extracting, or even better either use match, or or or_with to extract the value.

When Some contains multiple values than only the first argument is returned in scalar context.

my $x = Some(10)->get; # 10
my $x = None    ->get; # throws exception

my $x  = Some(1,2,3)->get; # 1
my @xs = Some(1,2,3)->get; # (1,2,3)

dump($opt, $inline=60, $depth=0) : $str

Recursivly traverses data-structures and creates a human readable dump from it.

$inline controls the amount of characters a data-structure is completely inlined into a single string without newlines. The higher the number the more compact your dump will be.

It currently has a bug as it also collaps whitespace in a string and it shouldn't do that. This could be avoided by setting $inline to 0. But consider that dumping in its current form is considered for debugging purposes, not for serializing data.

Currently it is not perfect. It only works with Perl Array/Hash and Sq Array/Hash and the Option type. Sq Array/Hash are just dumped as normal Perl Array/Hash. No other object is being dumped. It also does not dump any other object and has no configuration. Also doesn't detect cyclic data-structures. But for most practical data-structures it works good enough at the moment. Get's improved in the future.

printf "%s\n", $opt->dump;

dumpw($opt, $inline=60, $depth=0) : void

Same as dump but instead of returning the dump as a string, automatically prints it using warn.

$opt->dumpw;
warn $opt->dump, "\n";

MODULE FUNCTIONS

These functions are concipated to be called from the module level because they don't make sense to be called as a method. Usually because the first argument is not just an option. Here we have a list of options. But they also wouldn't make sense to be added to an array, or maybe it will?

You could call them in a functional-style, but then you also need to pass a dummy value as first argument.

Option->all_valid($array_of_opt);
Option::all_valid(undef, $array_of_opt);

Option->is_opt($any) : $bool

returns a truish value when $any is an optional value.

my $bool = Option->is_opt(Some({}));    # 1
my $bool = Option->is_opt(Some(1));     # 1
my $bool = Option->is_opt(None);        # 1
my $bool = Option->is_opt(Some(undef)); # 1
my $bool = Option->is_opt("");          # 0
my $bool = Option->is_opt(0);           # 0
my $bool = Option->is_opt([]);          # 0
my $bool = Option->is_opt({}):          # 0

WARNING: In probably 90% of all cases this is not the function you wanna use. Some new people that are introduced to the idea of optional values usually might use it like this.

if ( Option->is_opt($any) && $any->is_some ) {
    my $value = $any->get;
}

Don't do that.

First

Instead of Option->is_opt you can use Option::is_some or Option::is_none in a functional-style to also check $any value. Those functions are written in a way that they don't expect an optional value. You can directly do

if ( Option::is_some($any) ) {
    my $value = $any->get
}

This does the same and is shorter and clearer.

Second

Only call Option->is_opt when you expect that a function can return many different values of different types and you need to check if it is an optional value. Obviously when you know that something should return an optional then trust it and just use it as an optional. That's how dynamic-typing works.

That leads to the idea that very often you maybe want to check if you get Some value and do something with it. A first better aproach would be to use match instead.

my $double =
    $option->match(
        Some => sub($x) { $x * 2 },
        None => sub     { 0      },
    );

In this case $double will either be the double of $x or 0.

Third

The above assumes that you know a default value to be used when the value is None. But what do you do when you don't know a default value at this place? You could return an optional again.

my $opt_double =
    $option->match(
        Some => sub($x) { Some($x * 2) },
        None => sub     { None         },
    );

but, this function is exactly what map does! So instead write.

my $opt_double = $option->map(sub($x) { $x * 2 });

Consider map as a way to write code that only get's executed when you have Some value. Otherwise the code will not be executed.

When the code you pass to map returns an optional you should use bind instead of map.

Fourth

The idea of working with Optional values is that you keep working with an Option value as long as you can. Only at the very last step where you just need to access the value then you usually either use match, or, or_with, or_else, or_else_with to extract the value. Also get should be avoided if possible.

Use fold when you want to extract the value and then transform it to something else. Use iter when you want to do just some side-effect with the value.

Option->all_valid($array_of_opt)

Sometimes we have an array of optionals. Like [Some(1), Some(2), Some(3)]. But instead of an array of optionals we basically want to turn it inside-out. We want an optional array Some([1,2,3]) instead. The idea is that we only get Some array when all values are Some. As soon one value in the whole array is None we immediately just get None. So we can validate all values in a array at once.

Usually we get an array of optionals when we call an option generating function on every element of an array. For example by using Array::map.

# is_num() is a function automatically imported by Sq. It is just a shortcut
# for using Scalar::Util::looks_like_number()

# an option generating function
my $some_num = sub($str) { is_num($str) ? Some($str) : None };

# Some([12, 100, 13])
my $opt_nums =
    Option->all_valid(
        Array->new("12", "100", "13")->map($some_num)
    );

# None
my $opt_nums =
    Option->all_valid(
        Array->new("12", "100g", "13")->map($some_num)
    );

Option->all_valid_by($array, $f)

When you already have an array of optionals you use Option->all_valid. But when you want to run an optional generating function on every element of an array and then call Option->all_valid then you better use Option->all_valid_by that does both steps in one operation.

# an option generating function
my $some_num = sub($str) { is_num($str) ? Some($str) : None };

# Some([12, 100, 13])
my $opt_nums =
    Option->all_valid_by(Array->new("12", "100", "13"), $some_num);

# Some([12, 100, 13])
my $opt_nums =
    Option->all_valid_by(["12", "100", "13"], $some_num);

# Some([12, 100, 13])
my $opt_nums =
    Option->all_valid(
        Array->new("12", "100", "13")->map($some_num)
    );

Option->filter_valid($array)

Similar to Option->all_valid but instead of becoming None as soon one value is None in an array it just filters None values out. Additional an Sq array instead of an option is returned.

my $nums = Option->filter_valid([Some(1), Some(2), Some(3)]); # [1,2,3]
my $nums = Option->filter_valid([Some(1), Some(2),    None]); # [1,2]
my $nums = Option->filter_valid([None]);                      # []
my $nums = Option->filter_valid([]);                          # []

my $opt_nums = Option->all_valid([Some(1), Some(2), Some(3)]); # Some([1,2,3])
my $opt_nums = Option->all_valid([Some(1), Some(2),    None]); # None
my $opt_nums = Option->all_valid([None])                       # None
my $opt_nums = Option->all_valid([])                           # Some([])

Option->filter_valid_by($array, $f)

Like Option->filter_valid but additionaly runs function $f on every element of $array to create an option value first.

# an option generating function
my $some_num = sub($str) { is_num($str) ? Some($str) : None };

my $nums = Option->filter_valid   ([ map { $some_num->($_) } 1,2,3 ]);       # [1,2,3]
my $nums = Option->filter_valid   (Array->range(1,3)      ->map($some_num)); # [1,2,3]
my $nums = Option->filter_valid   (Array->new(qw/1 foo 2/)->map($some_num)); # [1,2]
my $nums = Option->filter_valid_by([qw/1 foo 2/], $some_num);                # [1,2]

Option->extract($any=undef) : ($bool, $x)

This is a helper function used when you want to create functions that expects anonymous functions that can return optional values or undef and still behaves correctly.

As an example look at Array::choose, you can use it in three ways.

my $array = $range->choose(sub($x) { $x % 2 == 0 ? Some($x*2) : None  });
my $array = $range->choose(sub($x) { $x % 2 == 0 ? $x*2       : undef });
my $array = $range->choose(sub($x) { $x % 2 == 0 ? $x*2       : ()    });

The lambda you pass to choose either can return Some/None or just a normal value or (undef/empty list) and choose still behaves correctly.

Here is the implementation of Array::choose that uses Option->extract to implement this kind of behaviour.

sub choose($array, $f_opt) {
    my $new = Array->new;
    my ($is_some, $v);
    for my $x ( @$array ) {
        ($is_some, $v) = Option->extract($f_opt->($x));
        push @$new, $v if $is_some;
    }
    return $new;
}

It basically works the following. Whatever value you pass to extract it returns a boolean flag indicating if you have some value or not, and the second value it returns is the actual value either directly or extracted from the optional.

Since the latest changes to Option, choose also could have been implemented this way.

sub choose($array, $f_opt) {
    my $new = Array->new;
    for my $x ( @$array ) {
        my $opt = Some($f_opt->($x));
        push @$new, $opt->get if $opt->is_some;
    }
    return $new;
}

because a call to Some doesn't wrap an option again and again now you just can wrap any function call that should return an optional in a Some call and then work with it like any optional value.

Option->extract is a slightly faster version of this because no creation of an optional value is involved.