# # 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 Chj::Serialize =head1 SYNOPSIS =head1 DESCRIPTION =head1 NOTE This is alpha software! Read the status section in the package README or on the L. =cut package Chj::Serialize; use strict; use warnings; use warnings FATAL => 'uninitialized'; use Exporter "import"; our @EXPORT = qw(new_Serialize_Closure); our @EXPORT_OK = qw(); our %EXPORT_TAGS = (all => [@EXPORT, @EXPORT_OK]); use Chj::TEST; { package Chj::Serializable::Closure; use FP::Struct ["env", "code_id"], 'FP::Struct::Show', 'FP::Abstract::Pure'; _END_ } { package Chj::Serialize::Closure; use PadWalker qw(closed_over set_closed_over); use FP::Repl::WithRepl qw(WithRepl_eval); use B::Deparse; use FP::Predicates ":all"; our $deparse = B::Deparse->new("-p", "-l", "-q"); use FP::Struct [ [\&is_hash, "_closure_generator_code_to_id"], [\&is_hash, "_id_to_closure_generator_code"], [\&is_hash, "_id_to_closure_generator"], [\&is_natural0, "current_id"] ], 'FP::Struct::Show'; sub next_id { my ($self) = @_; $$self{current_id}++ } sub id_to_closure_generator_code { my ($self, $id) = @_; $$self{_id_to_closure_generator_code}{$id} // die "unknown code_id $id"; } sub id_to_closure_generator { my ($self, $id) = @_; $$self{_id_to_closure_generator}{$id} //= &WithRepl_eval($self->id_to_closure_generator_code($id)) // die "eval error: $@"; #XX oh, want a WithRepl_xeval ? } sub serializable { my ($self, $fn) = @_; my $env = closed_over($fn); my $code = $deparse->coderef2text($fn); my $vars = [sort keys %$env]; my $closure_generator_code = 'sub { my (' . join(",", @$vars) . ') = @_; sub ' . $code . ' }'; # use this as key right now. my $id = $$self{_closure_generator_code_to_id}{$closure_generator_code} //= do { my $id = $self->next_id; $$self{_id_to_closure_generator_code}{$id} = $closure_generator_code; $id }; # XX exclude web server handle (request) etc. [hw btw in schem? xhu] Chj::Serializable::Closure->new($env, $id); } sub executable { my ($self, $serializable_closure) = @_; my ($env, $code_id) = ($serializable_closure->env, $serializable_closure->code_id); my @vals = map { $$env{$_} } sort keys %$env; $self->id_to_closure_generator($code_id)->(map {$$_} @vals); } _END_ } sub new_Serialize_Closure { Chj::Serialize::Closure->new({}, {}, {}, 0) } my $sc; my ($sfn, $sfn2); my $construct; TEST { $sc = new_Serialize_Closure; my $hello = "Hello"; my $unused = "Value"; $construct = sub { my ($a, $b) = @_; sub { my ($x) = @_; "$hello $x $a $b" } }; $sfn = $sc->serializable($construct->("the", "world", "so")); } bless( { 'env' => { '$hello' => \'Hello', '$a' => \'the', '$b' => \'world', }, 'code_id' => 0 }, 'Chj::Serializable::Closure' ); TEST { $sfn2 = $sc->serializable($construct->("fool", "!")); } bless( { 'env' => { '$hello' => \'Hello', '$a' => \'fool', '$b' => \'!', }, 'code_id' => 0 }, 'Chj::Serializable::Closure' ); TEST { $sc->executable($sfn)->("to") } 'Hello to the world'; TEST { $sc->executable($sfn2)->("you") } 'Hello you fool !'; 1