# # Copyright (c) 2015-2019 Christian Jaeger, copying@christianjaeger.ch # # This is free software, offered under either the same terms as perl 5 # or the terms of the Artistic License version 2 or the terms of the # MIT License (Expat version). See the file COPYING.md that came # bundled with this file. # =head1 NAME FP::Weak - utilities to weaken references =head1 SYNOPSIS use FP::Weak; sub foo { my $f; $f= sub { my ($n,$tot)=@_; $n < 100 ? &$f($n+1, $tot+$n) : $tot }; Weakened $f } is foo->(10, 0), 4905; # the subroutine returned from foo will not be leaked. =head1 DESCRIPTION =over 4 =item weaken `Scalar::Util`'s `weaken`, unless one of the `with_..` development utils are used (or `$FP::Weak::weaken` is changed). =item Weakened Calls `weaken ` after copying the reference, then returns the unweakened reference. =item Keep Protect from being weakened by accessing elements of `@_`. =back Optionally exported development utils: =over 4 =item noweaken ($var), noWeakened ($var) No-ops. The idea is to prefix the weakening ops with 'no' to disable them. =item warnweaken ($var), warnWeakened ($var) Give a warning in addition to the weakening operation. =item cluckweaken ($var), cluckWeakened ($var) Give a warning with backtrace in addition to the weakening operation. =item with_noweaken { code }, &with_noweaken ($proc) =item with_warnweaken { code } (and same as above) =item with_cluckweaken { code } Within their dynamic scope, globally change `weaken` to one of the alternatives =item do_weaken (1|0|"yes"|"no"|"on"|"off"|"warn"|"cluck") Turn weakening on and off (unscoped, 'persistently'). =back =head1 NOTE This is alpha software! Read the package README. =cut package FP::Weak; @ISA="Exporter"; require Exporter; @EXPORT=qw(weaken Weakened Keep); @EXPORT_OK=qw( do_weaken noweaken noWeakened with_noweaken warnweaken warnWeakened with_warnweaken cluckweaken cluckWeakened with_cluckweaken ); %EXPORT_TAGS=(all=>[@EXPORT,@EXPORT_OK]); use strict; use warnings; use warnings FATAL => 'uninitialized'; use Scalar::Util (); our $weaken= \&Scalar::Util::weaken; sub weaken ($) { goto &$weaken } # XX is there really no way (short of re-exporting everywhere with a # Chj::ruse approach) to avoid the extra function call cost? # protect a variable from being pruned by callees that prune their # arguments sub Keep ($) { my ($v)=@_; $v } # weaken a variable, but also provide a non-weakened reference to its # value as result sub Weakened ($) { my ($ref)= @_; weaken $_[0]; $ref } sub noweaken ($) { # noop } sub noWeakened ($) { $_[0] } sub with_noweaken (&) { local $weaken= \&noweaken; &{$_[0]}() } use Carp; sub warnweaken ($) { carp "weaken ($_[0])"; Scalar::Util::weaken ($_[0]); } sub warnWeakened ($) { carp "weaken ($_[0])"; Weakened ($_[0]); } sub with_warnweaken (&) { local $weaken= \&warnweaken; &{$_[0]}() } use Carp 'cluck'; sub cluckweaken ($) { cluck "weaken ($_[0])"; Scalar::Util::weaken ($_[0]); } sub cluckWeakened ($) { cluck "weaken ($_[0])"; Weakened ($_[0]); } sub with_cluckweaken (&) { local $weaken= \&cluckweaken; &{$_[0]}() } sub do_weaken ($) { my ($v)=@_; my $w= $v ? (+{ 1=> \&Scalar::Util::weaken, "yes"=> \&Scalar::Util::weaken, "no"=> \&noweaken, "on"=> \&Scalar::Util::weaken, "off"=> \&noweaken, "noweaken"=> \&noweaken, "warn"=> \&warnweaken, "warnweaken"=> \&warnweaken, "cluck"=> \&cluckweaken, "cluckweaken"=> \&cluckweaken, }->{$v} // die "do_weaken: unknown key '$v'") : \&noweaken; $weaken= $w } 1