← Index
NYTProf Performance Profile   « block view • line view • sub view »
For bin/hailo
  Run on Thu Oct 21 22:50:37 2010
Reported on Thu Oct 21 22:52:09 2010

Filename/mnt/stuff/src/my-cpan/hailo/lib/Hailo.pm
StatementsExecuted 125145 statements in 599ms
Subroutines
Calls P F Exclusive
Time
Inclusive
Time
Subroutine
2500011542ms50.4sHailo::::_learn_oneHailo::_learn_one
250001139.7ms54.7msHailo::::_engineHailo::_engine (xsub)
250001136.0ms82.6msHailo::::_tokenizerHailo::_tokenizer (xsub)
3114.29ms108msHailo::::_new_classHailo::_new_class
331756µs6.71msHailo::::_is_interactiveHailo::_is_interactive
111418µs11.2msHailo::::BEGIN@4Hailo::BEGIN@4
111287µs306µsHailo::::BEGIN@178Hailo::BEGIN@178
332164µs109msHailo::::__ANON__[lib/Hailo.pm:170]Hailo::__ANON__[lib/Hailo.pm:170]
11187µs51.9sHailo::::trainHailo::train
11166µs149µsHailo::::BEGIN@3Hailo::BEGIN@3
11157µs104µsHailo::::CORE:openHailo::CORE:open (opcode)
131153µs53µsHailo::::CORE:regcompHailo::CORE:regcomp (opcode)
66238µs47.3msHailo::::_storageHailo::_storage (xsub)
173234µs34µsHailo::::CORE:matchHailo::CORE:match (opcode)
11126µs103µsHailo::::DEMOLISHHailo::DEMOLISH
31121µs21µsHailo::::CORE:sortHailo::CORE:sort (opcode)
11118µs71µsHailo::::saveHailo::save
11115µs39µsHailo::::BEGIN@123Hailo::BEGIN@123
11114µs84µsHailo::::BEGIN@11Hailo::BEGIN@11
41113µs13µsHailo::::__ANON__[lib/Hailo.pm:111]Hailo::__ANON__[lib/Hailo.pm:111]
11113µs714µsHailo::::BEGIN@5Hailo::BEGIN@5
11113µs84µsHailo::::BEGIN@3.8Hailo::BEGIN@3.8
11112µs1.47msHailo::::BEGIN@9Hailo::BEGIN@9
11112µs63µsHailo::::BEGIN@7Hailo::BEGIN@7
11112µs53µsHailo::::BEGIN@8Hailo::BEGIN@8
11111µs368µsHailo::::BEGIN@6.10Hailo::BEGIN@6.10
3115µs5µsHailo::::PLUGINSHailo::PLUGINS (xsub)
1112µs2µsHailo::::engine_argsHailo::engine_args (xsub)
1112µs2µsHailo::::storage_argsHailo::storage_args (xsub)
1112µs2µsHailo::::tokenizer_argsHailo::tokenizer_args (xsub)
0000s0sHailo::::__ANON__[lib/Hailo.pm:102]Hailo::__ANON__[lib/Hailo.pm:102]
0000s0sHailo::::__ANON__[lib/Hailo.pm:145]Hailo::__ANON__[lib/Hailo.pm:145]
0000s0sHailo::::__ANON__[lib/Hailo.pm:146]Hailo::__ANON__[lib/Hailo.pm:146]
0000s0sHailo::::__ANON__[lib/Hailo.pm:151]Hailo::__ANON__[lib/Hailo.pm:151]
0000s0sHailo::::__ANON__[lib/Hailo.pm:155]Hailo::__ANON__[lib/Hailo.pm:155]
0000s0sHailo::::__ANON__[lib/Hailo.pm:182]Hailo::__ANON__[lib/Hailo.pm:182]
0000s0sHailo::::__ANON__[lib/Hailo.pm:33]Hailo::__ANON__[lib/Hailo.pm:33]
0000s0sHailo::::__ANON__[lib/Hailo.pm:65]Hailo::__ANON__[lib/Hailo.pm:65]
0000s0sHailo::::_train_fhHailo::_train_fh
0000s0sHailo::::learnHailo::learn
0000s0sHailo::::learn_replyHailo::learn_reply
0000s0sHailo::::replyHailo::reply
0000s0sHailo::::statsHailo::stats
Call graph for these subroutines as a Graphviz dot language file.
Line State
ments
Time
on line
Calls Time
in subs
Code
1package Hailo;
2
3484µs3304µs
# spent 84µs (13+71) within Hailo::BEGIN@3.8 which was called: # once (13µs+71µs) by Hailo::BEGIN@3 at line 3 # spent 149µs (66+84) within Hailo::BEGIN@3 which was called: # once (66µs+84µs) by Mouse::Util::_try_load_one_class at line 3
use 5.010;
# spent 149µs making 1 call to Hailo::BEGIN@3 # spent 84µs making 1 call to Hailo::BEGIN@3.8 # spent 71µs making 1 call to feature::import
42178µs211.2ms
# spent 11.2ms (418µs+10.8) within Hailo::BEGIN@4 which was called: # once (418µs+10.8ms) by Mouse::Util::_try_load_one_class at line 4
use autodie qw(open close);
# spent 11.2ms making 1 call to Hailo::BEGIN@4 # spent 7µs making 1 call to autodie::import
5233µs21.42ms
# spent 714µs (13+701) within Hailo::BEGIN@5 which was called: # once (13µs+701µs) by Mouse::Util::_try_load_one_class at line 5
use Any::Moose;
# spent 714µs making 1 call to Hailo::BEGIN@5 # spent 701µs making 1 call to Any::Moose::import
6230µs2725µs
# spent 368µs (11+357) within Hailo::BEGIN@6.10 which was called: # once (11µs+357µs) by Mouse::Util::_try_load_one_class at line 6
use Any::Moose 'X::StrictConstructor';
# spent 368µs making 1 call to Hailo::BEGIN@6.10 # spent 357µs making 1 call to Any::Moose::import
7227µs2115µs
# spent 63µs (12+52) within Hailo::BEGIN@7 which was called: # once (12µs+52µs) by Mouse::Util::_try_load_one_class at line 7
use Scalar::Util qw(blessed);
# spent 63µs making 1 call to Hailo::BEGIN@7 # spent 51µs making 1 call to Exporter::import
8229µs295µs
# spent 53µs (12+42) within Hailo::BEGIN@8 which was called: # once (12µs+42µs) by Mouse::Util::_try_load_one_class at line 8
use List::Util qw(first);
# spent 53µs making 1 call to Hailo::BEGIN@8 # spent 42µs making 1 call to Exporter::import
9245µs22.93ms
# spent 1.47ms (12µs+1.46) within Hailo::BEGIN@9 which was called: # once (12µs+1.46ms) by Mouse::Util::_try_load_one_class at line 9
use namespace::clean -except => 'meta';
# spent 1.47ms making 1 call to Hailo::BEGIN@9 # spent 1.46ms making 1 call to namespace::clean::import
10
11170µs
# spent 84µs (14+70) within Hailo::BEGIN@11 which was called: # once (14µs+70µs) by Mouse::Util::_try_load_one_class at line 19
use constant PLUGINS => [ qw[
# spent 70µs making 1 call to constant::import
12 Hailo::Engine::Default
13 Hailo::Storage::MySQL
14 Hailo::Storage::PostgreSQL
15 Hailo::Storage::SQLite
16 Hailo::Tokenizer::Chars
17 Hailo::Tokenizer::Words
18 Hailo::UI::ReadLine
192346µs184µs] ];
# spent 84µs making 1 call to Hailo::BEGIN@11
20
2115µs1351µshas brain => (
# spent 351µs making 1 call to Mouse::has
22 isa => 'Str',
23 is => 'rw',
24);
25
26has order => (
27 isa => 'Int',
28 is => 'rw',
29 default => 2,
30 trigger => sub {
31 my ($self, $order) = @_;
32 $self->_custom_order(1);
33 },
3417µs1269µs);
# spent 269µs making 1 call to Mouse::has
35
3614µs1263µshas _custom_order => (
# spent 263µs making 1 call to Mouse::has
37 isa => 'Bool',
38 is => 'rw',
39 default => 0,
40 init_arg => undef,
41 documentation => "Here so we can differentiate between the default value of order being explictly set and being set by default",
42);
43
4414µs1262µshas _custom_tokenizer_class => (
# spent 262µs making 1 call to Mouse::has
45 isa => 'Bool',
46 is => 'rw',
47 default => 0,
48 init_arg => undef,
49 documentation => "Here so we can differentiate between the default value of tokenizer_class being explictly set and being set by default",
50);
51
5213µs1272µshas save_on_exit => (
# spent 272µs making 1 call to Mouse::has
53 isa => 'Bool',
54 is => 'rw',
55 default => 1,
56);
57
58has brain_resource => (
59 documentation => "Alias for `brain' for backwards compatibility",
60 isa => 'Str',
61 is => 'rw',
62 trigger => sub {
63 my ($self, $brain) = @_;
64 $self->brain($brain);
65 },
6616µs1270µs);
# spent 270µs making 1 call to Mouse::has
67
68110µsmy %has = (
69 engine => {
70 name => 'Engine',
71 default => 'Default',
72 },
73 storage => {
74 name => 'Storage',
75 default => 'SQLite',
76 },
77 tokenizer => {
78 name => 'Tokenizer',
79 default => 'Words',
80 },
81 ui => {
82 name => 'UI',
83 default => 'ReadLine',
84 },
85);
86
87113µsfor my $k (keys %has) {
8832126µs my $name = $has{$k}->{name};
89 my $default = $has{$k}->{default};
90 my $method_class = "${k}_class";
91 my $method_args = "${k}_args";
92
93 # working classes
94 has "${k}_class" => (
95 isa => 'Str',
96 is => "rw",
97 default => $default,
98 ($k ~~ 'tokenizer'
99 ? (trigger => sub {
100 my ($self, $class) = @_;
101 $self->_custom_tokenizer_class(1);
102 })
10341.09ms : ())
# spent 1.09ms making 4 calls to Mouse::has, avg 273µs/call
104 );
105
106 # Object arguments
107 has "${k}_args" => (
108 documentation => "Arguments for the $name class",
109 isa => 'HashRef',
110 is => "ro",
111415µs
# spent 13µs within Hailo::__ANON__[lib/Hailo.pm:111] which was called 4 times, avg 3µs/call: # 4 times (13µs+0s) by Mouse::Object::new at line 78 of MouseX/Getopt/Basic.pm, avg 3µs/call
default => sub { +{} },
11241.08ms );
# spent 1.08ms making 4 calls to Mouse::has, avg 271µs/call
113
114 # Working objects
11542.13ms has "_${k}" => (
# spent 2.13ms making 4 calls to Mouse::has, avg 534µs/call
116 does => "Hailo::Role::$name",
117 lazy_build => 1,
118 is => 'ro',
119 init_arg => undef,
120 );
121
122 # Generate the object itself
1232325µs264µs
# spent 39µs (15+24) within Hailo::BEGIN@123 which was called: # once (15µs+24µs) by Mouse::Util::_try_load_one_class at line 123
no strict 'refs';
# spent 39µs making 1 call to Hailo::BEGIN@123 # spent 24µs making 1 call to strict::unimport
124
# spent 109ms (164µs+108) within Hailo::__ANON__[lib/Hailo.pm:170] which was called 3 times, avg 36.2ms/call: # once (78µs+47.1ms) by Hailo::_storage at line 221 of lib/Hailo/Command.pm # once (31µs+46.5ms) by Hailo::_tokenizer at line 288 # once (55µs+14.8ms) by Hailo::_engine at line 286
*{"_build__${k}"} = sub {
1259161µs my ($self) = @_;
126
127 my $obj = $self->_new_class(
128 $name,
129 $self->$method_class,
130 {
131 arguments => $self->$method_args,
132 ($k ~~ [ qw< engine storage > ]
133 ? (order => $self->order)
134 : ()),
135 ($k ~~ [ qw< engine > ]
136 ? (storage => $self->_storage)
137 : ()),
138 (($k ~~ [ qw< storage > ] and defined $self->brain)
139 ? (
140426µs15108ms hailo => do {
# spent 108ms making 3 calls to Hailo::_new_class, avg 36.1ms/call # spent 3µs making 2 calls to Hailo::Command::order, avg 2µs/call # spent 3µs making 2 calls to Hailo::Command::brain, avg 2µs/call # spent 3µs making 2 calls to Hailo::Command::tokenizer_class, avg 2µs/call # spent 2µs making 1 call to Hailo::engine_args # spent 2µs making 1 call to Hailo::Command::engine_class # spent 2µs making 1 call to Hailo::_storage # spent 2µs making 1 call to Hailo::storage_args # spent 2µs making 1 call to Hailo::tokenizer_args # spent 1µs making 1 call to Hailo::Command::storage_class
141 require Scalar::Util;
14212µs Scalar::Util::weaken(my $s = $self);
# spent 2µs making 1 call to Scalar::Util::weaken
143
144 my %callback = (
145 has_custom_order => sub { $s->_custom_order },
146 has_custom_tokenizer_class => sub { $s->_custom_tokenizer_class },
147 set_order => sub {
148 my ($db_order) = @_;
149 $s->order($db_order);
150 $s->_engine->order($db_order);
151 },
152 set_tokenizer_class => sub {
153 my ($db_tokenizer_class) = @_;
154 $s->tokenizer_class($db_tokenizer_class);
155 },
156 );
157
158 \%callback;
159 },
160 brain => $self->brain
161 )
162 : ()),
163 (($k ~~ [ qw< storage > ]
164 ? (tokenizer_class => $self->tokenizer_class)
165 : ()))
166 },
167 );
168
169 return $obj;
170 };
171}
172
173
# spent 108ms (4.29+104) within Hailo::_new_class which was called 3 times, avg 36.1ms/call: # 3 times (4.29ms+104ms) by Hailo::__ANON__[lib/Hailo.pm:170] at line 140, avg 36.1ms/call
sub _new_class {
1741899µs my ($self, $type, $class, $args) = @_;
175
176 my $pkg;
177999µs36µs if ($class =~ m[^\+(?<custom_plugin>.+)$]) {
# spent 6µs making 3 calls to Hailo::CORE:match, avg 2µs/call
17821.19ms1306µs
# spent 306µs (287+20) within Hailo::BEGIN@178 which was called: # once (287µs+20µs) by Mouse::Util::_try_load_one_class at line 178
$pkg = $+{custom_plugin};
# spent 306µs making 1 call to Hailo::BEGIN@178
179 } else {
18035µs my @plugins = @{ $self->PLUGINS };
# spent 5µs making 3 calls to Hailo::PLUGINS, avg 2µs/call
181 # Be fuzzy about includes, e.g. DBD::SQLite or SQLite or sqlite will go
18213176µs2678µs $pkg = first { / $type : .* : $class /ix }
# spent 53µs making 13 calls to Hailo::CORE:regcomp, avg 4µs/call # spent 25µs making 13 calls to Hailo::CORE:match, avg 2µs/call
1836193µs sort { length $a <=> length $b }
# spent 172µs making 3 calls to List::Util::first, avg 57µs/call # spent 21µs making 3 calls to Hailo::CORE:sort, avg 7µs/call
184 @plugins;
185
186 unless ($pkg) {
187 local $" = ', ';
188 my @p = grep { /$type/ } @plugins;
189 die "Couldn't find a class name matching '$class' in plugins '@p'";
190 }
191 }
192
1933105µs315µs if (Any::Moose::moose_is_preferred()) {
# spent 15µs making 3 calls to Any::Moose::moose_is_preferred, avg 5µs/call
194 require Class::MOP;
195 eval { Class::MOP::load_class($pkg) };
196 } else {
197 eval qq[require $pkg];
# spent 100µs executing statements in string eval # spent 80µs executing statements in string eval # spent 69µs executing statements in string eval
198 }
199 die $@ if $@;
200
2011204µs301.06ms return $pkg->new(%$args);
# spent 621µs making 3 calls to Mouse::Object::new, avg 207µs/call # spent 292µs making 3 calls to Mouse::Meta::Class::_calculate_all_attributes, avg 97µs/call # spent 85µs making 1 call to Hailo::Engine::Default::BUILD # spent 25µs making 1 call to Hailo::Role::Tokenizer::BUILD # spent 15µs making 10 calls to Mouse::Meta::TypeConstraint::_compiled_type_constraint, avg 2µs/call # spent 7µs making 3 calls to Mouse::Meta::Class::strict_constructor, avg 2µs/call # spent 5µs making 1 call to Hailo::Role::Tokenizer::__ANON__[lib/Hailo/Role/Tokenizer.pm:15] # spent 5µs making 3 calls to Mouse::Meta::Class::is_immutable, avg 2µs/call # spent 4µs making 3 calls to Mouse::Meta::Class::is_anon_class, avg 1µs/call # spent 3µs making 2 calls to Mouse::Meta::Attribute::default, avg 2µs/call
202}
203
204
# spent 71µs (18+53) within Hailo::save which was called: # once (18µs+53µs) by Hailo::DEMOLISH at line 334
sub save {
205317µs my ($self, @args) = @_;
206253µs $self->_storage->save(@args);
# spent 51µs making 1 call to Hailo::Storage::SQLite::save # spent 2µs making 1 call to Hailo::_storage
207 return;
208}
209
210
# spent 51.9s (87µs+51.9) within Hailo::train which was called: # once (87µs+51.9s) by Hailo::Command::run at line 260 of lib/Hailo/Command.pm
sub train {
211534µs my ($self, $input) = @_;
212
213253.0ms $self->_storage->start_training();
# spent 53.0ms making 1 call to Hailo::Storage::SQLite::start_training # spent 1µs making 1 call to Hailo::_storage
214
21535µs given ($input) {
216 # With STDIN
217 when (not ref and defined and $_ eq '-') {
218 die "You must provide STDIN when training from '-'" if $self->_is_interactive(*STDIN);
219 $self->_train_fh(*STDIN);
220 }
221 # With a filehandle
222 when (ref eq 'GLOB') {
223 $self->_train_fh($input);
224 }
225 # With a file
226237µs when (not ref) {
227129µs open my $fh, '<:encoding(utf8)', $input;
# spent 29µs making 1 call to Hailo::__ANON__[(eval 30)[Fatal.pm:1188]:47]
228151.8s $self->_train_fh($fh, $input);
# spent 51.8s making 1 call to Hailo::Command::_train_fh
229 }
230 # With an Array
231 when (ref eq 'ARRAY') {
232 $self->_learn_one($_) for @$input;
233 }
234 # With something naughty
235 default {
236 die "Unknown input: $input";
237 }
238 }
239
24024.47ms $self->_storage->stop_training();
# spent 4.47ms making 1 call to Hailo::Storage::SQLite::stop_training # spent 3µs making 1 call to Hailo::_storage
241
242 return;
243}
244
245sub _train_fh {
246 my ($self, $fh, $filename) = @_;
247
248 while (my $line = <$fh>) {
249 chomp $line;
250 $self->_learn_one($line);
251 }
252
253 return;
254}
255
256sub learn {
257 my ($self, $input) = @_;
258 my $inputs;
259
260 given ($input) {
261 when (not defined) {
262 die "Cannot learn from undef input";
263 }
264 when (not ref) {
265 $inputs = [$input];
266 }
267 # With an Array
268 when (ref eq 'ARRAY') {
269 $inputs = $input
270 }
271 default {
272 die "Unknown input: $input";
273 }
274 }
275
276 my $storage = $self->_storage;
277
278 $storage->start_learning();
279 $self->_learn_one($_) for @$inputs;
280 $storage->stop_learning();
281 return;
282}
283
284
# spent 50.4s (542ms+49.9) within Hailo::_learn_one which was called 25000 times, avg 2.02ms/call: # 25000 times (542ms+49.9s) by Hailo::Command::train_progress at line 334 of lib/Hailo/Command.pm, avg 2.02ms/call
sub _learn_one {
285125000595ms my ($self, $input) = @_;
286119µs2500469.7ms my $engine = $self->_engine;
# spent 54.7ms making 25000 calls to Hailo::_engine, avg 2µs/call # spent 14.9ms making 1 call to Hailo::__ANON__[lib/Hailo.pm:170] # spent 119µs making 1 call to Mouse::Util::TypeConstraints::__ANON__[Mouse/Util/TypeConstraints.pm:217] # spent 2µs making 1 call to Mouse::Meta::Attribute::builder # spent 2µs making 1 call to Mouse::Meta::TypeConstraint::_compiled_type_constraint
287
288122µs5000411.5s my $tokens = $self->_tokenizer->make_tokens($input);
# spent 11.4s making 25000 calls to Hailo::Tokenizer::Words::make_tokens, avg 454µs/call # spent 82.6ms making 25000 calls to Hailo::_tokenizer, avg 3µs/call # spent 46.5ms making 1 call to Hailo::__ANON__[lib/Hailo.pm:170] # spent 119µs making 1 call to Mouse::Util::TypeConstraints::__ANON__[Mouse/Util/TypeConstraints.pm:217] # spent 2µs making 1 call to Mouse::Meta::TypeConstraint::_compiled_type_constraint # spent 2µs making 1 call to Mouse::Meta::Attribute::builder
2892500038.4s $engine->learn($tokens);
# spent 38.4s making 25000 calls to Hailo::Engine::Default::learn, avg 1.54ms/call
290
291 return;
292}
293
294sub learn_reply {
295 my ($self, $input) = @_;
296 $self->learn($input);
297 return $self->reply($input);
298}
299
300sub reply {
301 my ($self, $input) = @_;
302
303 my $storage = $self->_storage;
304 # start_training() hasn't been called so we can't guarentee that
305 # the storage has been engaged at this point. This must be called
306 # before ->_engine() is called anywhere to ensure that the
307 # lazy-loading in the engine works.
308 $storage->_engage() unless $storage->_engaged;
309
310 my $engine = $self->_engine;
311 my $tokenizer = $self->_tokenizer;
312
313 my $reply;
314 if (defined $input) {
315 my $tokens = $tokenizer->make_tokens($input);
316 $reply = $engine->reply($tokens);
317 }
318 else {
319 $reply = $engine->reply();
320 }
321
322 return unless defined $reply;
323 return $tokenizer->make_output($reply);
324}
325
326sub stats {
327 my ($self) = @_;
328
329 return $self->_storage->totals();
330}
331
332
# spent 103µs (26+77) within Hailo::DEMOLISH which was called: # once (26µs+77µs) by Mouse::Object::DESTROY at line 0 of bin/hailo
sub DEMOLISH {
333331µs my ($self) = @_;
334377µs $self->save() if blessed $self->{_storage} and $self->save_on_exit;
# spent 71µs making 1 call to Hailo::save # spent 4µs making 1 call to Scalar::Util::blessed # spent 2µs making 1 call to Hailo::Command::save_on_exit
335 return;
336}
337
338
# spent 6.71ms (756µs+5.95) within Hailo::_is_interactive which was called 3 times, avg 2.24ms/call: # once (738µs+5.84ms) by Hailo::Command::__ANON__[lib/Hailo/Command.pm:59] at line 58 of lib/Hailo/Command.pm # once (9µs+59µs) by Hailo::Command::__ANON__[lib/Hailo/Command.pm:298] at line 293 of lib/Hailo/Command.pm # once (10µs+57µs) by Hailo::Command::run at line 249 of lib/Hailo/Command.pm
sub _is_interactive {
3396197µs require IO::Interactive;
3403184µs return IO::Interactive::is_interactive();
# spent 184µs making 3 calls to IO::Interactive::is_interactive, avg 61µs/call
341}
342
343135µs2158µs__PACKAGE__->meta->make_immutable;
# spent 142µs making 1 call to Mouse::Meta::Class::make_immutable # spent 15µs making 1 call to Hailo::meta
344
345=encoding utf8
346
347=head1 NAME
348
349Hailo - A pluggable Markov engine analogous to MegaHAL
350
351=head1 SYNOPSIS
352
353This is the synopsis for using Hailo as a module. See L<hailo> for
354command-line invocation.
355
356 # Hailo requires Perl 5.10
357 use 5.010;
358 use Any::Moose;
359 use Hailo;
360
361 # Construct a new in-memory Hailo using the SQLite backend. See
362 # backend documentation for other options.
363 my $hailo = Hailo->new;
364
365 # Various ways to learn
366 my @train_this = ("I like big butts", "and I can not lie");
367 $hailo->learn(\@train_this);
368 $hailo->learn($_) for @train_this;
369
370 # Heavy-duty training interface. Backends may drop some safety
371 # features like journals or synchronous IO to train faster using
372 # this mode.
373 $hailo->train("megahal.trn");
374 $hailo->train($filehandle);
375
376 # Make the brain babble
377 say $hailo->reply("hello good sir.");
378 # Just say something at random
379 say $hailo->reply();
380
381=head1 DESCRIPTION
382
383Hailo is a fast and lightweight markov engine intended to replace
384L<AI::MegaHAL|AI::MegaHAL>. It has a L<Mouse|Mouse> (or
385L<Moose|Moose>) based core with pluggable
386L<storage|Hailo::Role::Storage>, L<tokenizer|Hailo::Role::Tokenizer>
387and L<engine|Hailo::Role::Engine> backends.
388
389It is similar to MegaHAL in functionality, the main differences (with the
390default backends) being better scalability, drastically less memory usage,
391an improved tokenizer, and tidier output.
392
393With this distribution, you can create, modify, and query Hailo brains. To
394use Hailo in event-driven POE applications, you can use the
395L<POE::Component::Hailo|POE::Component::Hailo> wrapper. One example is
396L<POE::Component::IRC::Plugin::Hailo|POE::Component::IRC::Plugin::Hailo>,
397which implements an IRC chat bot.
398
399=head2 Etymology
400
401I<Hailo> is a portmanteau of I<HAL> (as in MegaHAL) and
402L<failo|http://identi.ca/failo>.
403
404=head1 Backends
405
406Hailo supports pluggable L<storage|Hailo::Role::Storage> and
407L<tokenizer|Hailo::Role::Tokenizer> backends, it also supports a
408pluggable L<UI|Hailo::Role::UI> backend which is used by the L<hailo>
409command-line utility.
410
411=head2 Storage
412
413Hailo can currently store its data in either a
414L<SQLite|Hailo::Storage::SQLite>,
415L<PostgreSQL|Hailo::Storage::PostgreSQL> or
416L<MySQL|Hailo::Storage::MySQL> database. Some NoSQL backends were
417supported in earlier versions, but they were removed as they had no
418redeeming quality.
419
420SQLite is the primary target for Hailo. It's much faster and uses less
421resources than the other two. It's highly recommended that you use it.
422
423See L<Hailo::Storage/"Comparison of backends"> for benchmarks showing
424how the various backends compare under different workloads, and how
425you can create your own.
426
427=head2 Tokenizer
428
429By default Hailo will use L<the word
430tokenizer|Hailo::Tokenizer::Words> to split up input by whitespace,
431taking into account things like quotes, sentence terminators and more.
432
433There's also a L<the character
434tokenizer|Hailo::Tokenizer::Chars>. It's not generally useful for a
435conversation bot but can be used to e.g. generate new words given a
436list of existing words.
437
438=head1 UPGRADING
439
440Hailo makes no promises about brains generated with earlier versions
441being compatable with future version and due to the way Hailo works
442there's no practical way to make that promise. Learning in Hailo is
443lossy so an accurate conversion is impossible.
444
445If you're maintaining a Hailo brain that you want to keep using you
446should save the input you trained it on and re-train when you upgrade.
447
448Hailo is always going to lose information present in the input you
449give it. How input tokens get split up and saved to the storage
450backend depends on the version of the tokenizer being used and how
451that input gets saved to the database.
452
453For instance if an earlier version of Hailo tokenized C<"foo+bar">
454simply as C<"foo+bar"> but a later version split that up into
455C<"foo", "+", "bar">, then an input of C<"foo+bar are my favorite
456metasyntactic variables"> wouldn't take into account the existing
457C<"foo+bar"> string in the database.
458
459Tokenizer changes like this would cause the brains to accumulate
460garbage and would leave other parts in a state they wouldn't otherwise
461have gotten into.
462
463There have been more drastic changes to the database format itself in
464the past.
465
466Having said all that the database format and the tokenizer are
467relatively stable. At the time of writing 0.33 is the latest release
468and it's compatable with brains down to at least 0.17. If you're
469upgrading and there isn't a big notice about the storage format being
470incompatable in the F<Changes> file your old brains will probably work
471just fine.
472
473=head1 ATTRIBUTES
474
475=head2 C<brain>
476
477The name of the brain (file name, database name) to use as storage.
478There is no default. Whether this gets used at all depends on the
479storage backend, currently only SQLite uses it.
480
481=head2 C<save_on_exit>
482
483A boolean value indicating whether Hailo should save its state before
484its object gets destroyed. This defaults to true and will simply call
485L<save|/save> at C<DEMOLISH> time.
486
487See L<Hailo::Storage::SQLite/"in_memory"> for how the SQLite backend
488uses this option in conjunction with its C<in_memory> option.
489
490=head2 C<order>
491
492The Markov order (chain length) you want to use for an empty brain.
493The default is 2.
494
495=head2 C<engine_class>
496
497=head2 C<storage_class>
498
499=head2 C<tokenizer_class>
500
501=head2 C<ui_class>
502
503A a short name name of the class we use for the engine, storage,
504tokenizer or ui backends.
505
506By default this is B<Default> for the engine, B<SQLite> for storage,
507B<Words> for the tokenizer and B<ReadLine> for the UI. The UI backend
508is only used by the L<hailo> command-line interface.
509
510You can only specify the short name of one of the packages Hailo
511itself ships with. If you need another class then just prefix the
512package with a plus (Catalyst style), e.g. C<+My::Foreign::Tokenizer>.
513
514=head2 C<engine_args>
515
516=head2 C<storage_args>
517
518=head2 C<tokenizer_args>
519
520=head2 C<ui_args>
521
522A C<HashRef> of arguments for engine/storage/tokenizer/ui
523backends. See the documentation for the backends for what sort of
524arguments they accept.
525
526=head1 METHODS
527
528=head2 C<new>
529
530This is the constructor. It accepts the attributes specified in
531L</ATTRIBUTES>.
532
533=head2 C<learn>
534
535Takes a string or an array reference of strings and learns from them.
536
537=head2 C<train>
538
539Takes a filename, filehandle or array reference and learns from all its
540lines. If a filename is passed, the file is assumed to be UTF-8 encoded.
541Unlike L<C<learn>|/learn>, this method sacrifices some safety (disables
542the database journal, fsyncs, etc) for speed while learning.
543
544=head2 C<reply>
545
546Takes an optional line of text and generates a reply that might be relevant.
547
548=head2 C<learn_reply>
549
550Takes a string argument, learns from it, and generates a reply that
551might be relevant. This is equivalent to calling L<learn|/learn>
552followed by L<reply|/reply>.
553
554=head2 C<save>
555
556Tells the underlying storage backend to L<save its
557state|Hailo::Role::Storage/"save">, any arguments to this method will
558be passed as-is to the backend.
559
560=head2 C<stats>
561
562Takes no arguments. Returns the number of tokens, expressions, previous
563token links and next token links.
564
565=head1 SUPPORT
566
567You can join the IRC channel I<#hailo> on FreeNode if you have questions.
568
569=head1 BUGS
570
571Bugs, feature requests and other issues are tracked in L<Hailo's RT on
572rt.cpan.org|https://rt.cpan.org/Dist/Display.html?Name=Hailo>
573
574=head1 SEE ALSO
575
576=over
577
578=item * L<POE::Component::Hailo> - A non-blocking POE wrapper around Hailo
579
580=item * L<POE::Component::IRC::Plugin::Hailo> - A Hailo IRC bot plugin
581
582=item * L<http://github.com/hinrik/failo> - Failo, an IRC bot that uses Hailo
583
584=item * L<http://github.com/bingos/gumbybrain> - GumbyBRAIN, a more famous IRC bot that uses Hailo
585
586=item * L<Hailo::UI::Web> - A L<Catalyst> and jQuery powered web
587interface to Hailo available at L<hailo.nix.is|http://hailo.nix.is>
588and as L<hailo-ui-web|http://github.com/avar/hailo-ui-web> on
589L<GitHub|http://github.com>
590
591=item * L<HALBot> - Another L<Catalyst> Dojo powered web interface to
592Hailo available at L<bifurcat.es|http://bifurcat.es/> and as
593L<halbot-on-the-web|http://gitorious.org/halbot-on-the-web/halbot-on-the-web>
594at L<gitorious|http://gitorious.org>
595
596=item * L<http://github.com/pteichman/cobe> - cobe, a Python port of MegaHAL "inspired by the success of Hailo"
597
598=back
599
600=head1 LINKS
601
602=over
603
604=item * L<hailo.org|http://hailo.org> - Hailo's website
605
606=item * L<http://bit.ly/hailo_rewrite_of_megahal> - Hailo: A Perl rewrite of
607MegaHAL, A blog posting about the motivation behind Hailo
608
609=item * L<http://blogs.perl.org/users/aevar_arnfjor_bjarmason/hailo/> -
610More blog posts about Hailo on E<AElig>var ArnfjE<ouml>rE<eth>
611Bjarmason's L<blogs.perl.org|http://blogs.perl.org> blog
612
613=item * Hailo on L<freshmeat|http://freshmeat.net/projects/hailo> and
614L<ohloh|https://www.ohloh.net/p/hailo>
615
616=back
617
618=head1 AUTHORS
619
620Hinrik E<Ouml>rn SigurE<eth>sson, hinrik.sig@gmail.com
621
622E<AElig>var ArnfjE<ouml>rE<eth> Bjarmason <avar@cpan.org>
623
624=head1 LICENSE AND COPYRIGHT
625
626Copyright 2010 Hinrik E<Ouml>rn SigurE<eth>sson and
627E<AElig>var ArnfjE<ouml>rE<eth> Bjarmason <avar@cpan.org>
628
629This program is free software, you can redistribute it and/or modify
630it under the same terms as Perl itself.
631
632129µs14.62ms=cut
 
# spent 34µs within Hailo::CORE:match which was called 17 times, avg 2µs/call: # 13 times (25µs+0s) by List::Util::first at line 182, avg 2µs/call # 3 times (6µs+0s) by Hailo::_new_class at line 177, avg 2µs/call # once (3µs+0s) by Hailo::__ANON__[(eval 30)[/home/hinrik/perl5/perlbrew/perls/perl-5.13.5/lib/5.13.5/Fatal.pm:1188]:47] at line 14 of (eval 30)[Fatal.pm:1188]
sub Hailo::CORE:match; # opcode
# spent 104µs (57+47) within Hailo::CORE:open which was called: # once (57µs+47µs) by Hailo::__ANON__[(eval 29)[/home/hinrik/perl5/perlbrew/perls/perl-5.13.5/lib/5.13.5/Fatal.pm:1102]:127] at line 109 of (eval 29)[Fatal.pm:1102]
sub Hailo::CORE:open; # opcode
# spent 53µs within Hailo::CORE:regcomp which was called 13 times, avg 4µs/call: # 13 times (53µs+0s) by List::Util::first at line 182, avg 4µs/call
sub Hailo::CORE:regcomp; # opcode
# spent 21µs within Hailo::CORE:sort which was called 3 times, avg 7µs/call: # 3 times (21µs+0s) by Hailo::_new_class at line 183, avg 7µs/call
sub Hailo::CORE:sort; # opcode
# spent 5µs within Hailo::PLUGINS which was called 3 times, avg 2µs/call: # 3 times (5µs+0s) by Hailo::_new_class at line 180, avg 2µs/call
sub Hailo::PLUGINS; # xsub
# spent 54.7ms (39.7+15.0) within Hailo::_engine which was called 25000 times, avg 2µs/call: # 25000 times (39.7ms+15.0ms) by Hailo::_learn_one at line 286, avg 2µs/call
sub Hailo::_engine; # xsub
# spent 47.3ms (38µs+47.3) within Hailo::_storage which was called 6 times, avg 7.88ms/call: # once (29µs+47.3ms) by Hailo::Command::__ANON__[lib/Hailo/Command.pm:234] at line 221 of lib/Hailo/Command.pm # once (3µs+0s) by Hailo::train at line 240 # once (2µs+0s) by Hailo::Command::run at line 249 of lib/Hailo/Command.pm # once (2µs+0s) by Hailo::save at line 206 # once (2µs+0s) by Hailo::__ANON__[lib/Hailo.pm:170] at line 140 # once (1µs+0s) by Hailo::train at line 213
sub Hailo::_storage; # xsub
# spent 82.6ms (36.0+46.7) within Hailo::_tokenizer which was called 25000 times, avg 3µs/call: # 25000 times (36.0ms+46.7ms) by Hailo::_learn_one at line 288, avg 3µs/call
sub Hailo::_tokenizer; # xsub
# spent 2µs within Hailo::engine_args which was called: # once (2µs+0s) by Hailo::__ANON__[lib/Hailo.pm:170] at line 140
sub Hailo::engine_args; # xsub
# spent 2µs within Hailo::storage_args which was called: # once (2µs+0s) by Hailo::__ANON__[lib/Hailo.pm:170] at line 140
sub Hailo::storage_args; # xsub
# spent 2µs within Hailo::tokenizer_args which was called: # once (2µs+0s) by Hailo::__ANON__[lib/Hailo.pm:170] at line 140
sub Hailo::tokenizer_args; # xsub