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.