Option->extract_array(...)

Here i wanna describe in more detail why those functions exists, what they do, and more important, why they are how they are. I guess by understanding what leads to the development of this function it shows more insight how to use Option in Sq and what you can do.

Some() at the beginning

At the beginning Some returned a valid value for every value, except for undef. This behaviour is somehow nice as you can wrap the result of any function into Some and always get an option back. You just can write.

my $opt = Some( func() );

This is very convenient because there are a lot of Perl functions throught Perl in a lot of modules that indicate an error. So turning any non option aware function into an option aware is pretty straightforward.

But the idea in Sq is that Option should be used. Let's say someone upgrade its func() function and now starting to return Option. Now that code doesn't work anymore.

Some() detects Option

It didn't worked because Some when func() returns an Option and now you wrap it again in Some you suddenly got a Some(Some($value)) or maybe a Some(None).

At this point I thought about the idea if wrapping Option anyway makes sense.

I came to the conclusion it didn't. Values like Some(Some(Some(10))) have no additional information compared to Some(10). So I changed Some that it detects for an optional. Now we still can write.

my $opt = Some( func() );

but func() is free to decide if it either return a value/undef or returns Some/None and it just works. $opt will now always be just a single Option, nothing wrapped and you can work with it.

Option->extract

This brought me to the implementation of

my ($is_some, $x) = Option->extract( func() );

So here is the idea. While the above works fine it can have a performance impact. Because always an Option value must be created. But let's say that you just want to check if we had a valid value or not, and just want to get the value. We don't need to return an Option besides that don't call any methods on it. So creating an Option seems a waste when func() returns a normal valid value or undef.

We could optimize that by calling func() and then check the return value. When it is an Option we just work with it. If not we just use the value and check for defined instead.

And this is exactly what Option->extract does. So you/me don't have to implement this again and again.

Option->extract is somehow similar to Go language as now it returns two variables. The first one indicating if we had a valid value, and the second variable is the actual value.

The pattern is usually

my ($is_some, $x) = Option->extract(func());
if ( $is_some ) {
    # do something with x
}

my ($is_some, $x) = Option->extract(undef);    # 0
my ($is_some, $x) = Option->extract(None);     # 0
my ($is_some, $x) = Option->extract(10);       # 1, 10
my ($is_some, $x) = Option->extract(Some(10)); # 1, 10

Option->extract works bei either unpacking the option or it does a defined check for you. This means it unifies two different syntaxes and ideas about an empty value and it will just work. In the case when func() does not return an Option it is a little bit faster as no Option must be created.

Option->extract_array()

This leads to the next function. Because Perl also allows to return multiple values instead of just one single value. And at some places this feature was used.

For example in Array->unfold you pass a lambda that should return two values. The $x value used in the array and $state for the next state to build the next value.

Here returning the empty list or undef was used to decide if we want to abort the generation. But wouldn't it be nice it also could work with Option? Because this differentation between a valid value or not is exactly what Option is all about.

But Option is limited to only a single value. So how do we return multiple ones? Well by wrapping it in an array.

my ($is_some, @xs) = Option->extract_array();             # 0
my ($is_some, @xs) = Option->extract_array(undef);        # 0
my ($is_some, @xs) = Option->extract_array(None);         # 0
my ($is_some, @xs) = Option->extract_array(5,10);         # 1,5,10
my ($is_some, @xs) = Option->extract_array(Some([5,10])); # 1,5,10
my ($is_some, @xs) = Option->extract_array(10)            # 1,10

At first I always expected that a user must pass an array to Some Some([10]) but why create an array when we anyway only have a single value? Instead I also allowed that a single value can be passed.

So it is very like a DWIM (Do What I Mean) function. A user can return no value at all by an empty return a single undef or None and all of them will turn into $is_some being 0.

Then you can decide to return Some($value) for a single value or return Some([$x,$x,$x]) and return multiple values. And you don't have to write the whole type-checking conditional logic yourself.