File Coverage

blib/lib/Functional/Types.pm
Criterion Covered Total %
statement 18 544 3.3
branch 0 312 0.0
condition 0 32 0.0
subroutine 6 57 10.5
pod 0 49 0.0
total 24 994 2.4


line stmt bran cond sub pod time code
1             package Functional::Types;
2              
3 1     1   23105 use warnings;
  1         2  
  1         45  
4 1     1   6 use strict;
  1         3  
  1         48  
5 1     1   6 no strict 'subs';
  1         7  
  1         33  
6 1     1   15 use v5.16;
  1         3  
  1         42  
7 1     1   681 use version; our $VERSION = version->declare('v0.0.1');
  1         2280  
  1         7  
8              
9             require Exporter;
10             our @ISA = qw(Exporter);
11              
12             our %EXPORT_TAGS = (
13             'all' => [
14             qw(
15              
16             )
17             ]
18             );
19              
20             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
21              
22             our @EXPORT = (
23             qw(
24             Scalar
25             Array
26             Map
27             Tuple
28             Record
29             Variant
30             variant
31             Function
32             Int
33             Float
34             String
35             Bool
36             True
37             False
38             type
39             newtype
40             typename
41             cast
42             bind
43             let
44             untype
45             show
46             read
47             a b c d t
48             )
49              
50             #,@{TypeChecking::API::EXPORT}
51             );
52              
53 1     1   1096 use Data::Dumper;
  1         14214  
  1         8884  
54             our $VV = 1;
55             our $FIXME = 0;
56              
57             =encoding utf-8
58              
59             =head1 NAME
60              
61             Functional::Types - a Haskell-inspired type system for Perl
62              
63             =head1 SYNOPSIS
64              
65             use Functional::Types;
66              
67             sub ExampleType { newtype }
68             sub MkExampleType { typename ExampleType, Record(Int,String), @_ }
69              
70             type my $v = ExampleType;
71             bind $v, MkExampleType(42,"forty-two");
72             say show $v;
73             my $uv = untype $v;
74              
75             =head1 DESCRIPTION
76              
77             Functional::Types provides a runtime type system for Perl, the main purpose is to allow type checking and have self-documenting data structures. It is strongly influenced by Haskell's type system. More details are below, but at the moment they are not up-to-date. The /t folder contains examples of the use of each type.
78              
79             =head1 AUTHOR
80              
81             Wim Vanderbauwhede EWim.Vanderbauwhede@mail.beE
82              
83             =head1 COPYRIGHT
84              
85             Copyright 2015- Wim Vanderbauwhede
86              
87             =head1 LICENSE
88              
89             This library is free software; you can redistribute it and/or modify
90             it under the same terms as Perl itself.
91              
92             =head1 SEE ALSO
93              
94             =cut
95              
96             # Some types don't have constructors. e.g. Int. As in Perl scalars are not typed at all, in order to do type checking we must do one of the following:
97             # What the newtype() call does is create an entry in the type table:
98              
99             sub AUTOLOAD {
100 0     0     our $AUTOLOAD;
101 0           my $t = $AUTOLOAD;
102 0           $t =~ s/^\w+:://;
103 0           return [ $t, [@_] ];
104             }
105              
106             ################################################################################
107             # e.g. type my $x = IntVar;
108             sub type {
109             # say "sub type():".Dumper(@_);
110 0     0 0   my $tn = $_[0];
111 0 0         if (@_>1) {
112 0           my $tn_args=[];
113 0           for my $arg (@_) {
114 0 0         if (ref($arg) !~/ARRAY|Array|Map|Tuple/) {
115 0 0         say '# FIXME: What about proper types?' if $FIXME;
116 0           push @{$tn_args},[$arg,[]];
  0            
117             } else {
118 0           push @{$tn_args},$arg;
  0            
119             }
120             }
121 0           $tn = bless(['Function', $tn_args],'Function') ; # So type my $f = Int => Int => Int should work OK
122             }
123 0           $_[0] = bless( { 'Type' => $tn, 'Val' => undef }, 'Functional::Types' );
124             # die 'BOOM:'.Dumper(@_);
125             }
126              
127             sub typename {
128 0     0 0   my @c = caller(1);
129 0           my $t = $c[3];
130 0           $t =~ s/^.+:://;
131 0           return [ $t, [@_] ];
132             }
133              
134 0     0 0   sub a { return 'a'; }
135 0     0 0   sub b { return 'b'; }
136 0     0 0   sub c { return 'c'; }
137 0     0 0   sub d { return 'd'; }
138 0     0 0   sub t { return 't'; }
139              
140             =head1 NEWTYPE
141              
142             The function of newtype is to glue typename information together with the constructor information, and typecheck the arguments to the constructor.
143             I think it is best to treat all cases separately:
144              
145             - Primitive types: e.g. sub ArgType { newtype String,@_ } # Does ArgType expect a String or a bare value? I guess a bare value is better?
146             - Record types: e.g. sub MkVarDecl { newtype VarDecl, Record( acc1 => ArgType, acc2 => Int), @_ }
147             - Variant types: e.g. sub Just { newtype Maybe(a), Variant(a), @_ }
148             - Map type: sub HashTable { newtype Map(String,Int), @_ } is a primitive type
149             - Array type: sub IntList { newtype Array(Int), @_ } is a primitive type
150              
151             I expect String to be called and it will return ['$',String] so ArgType(String("str")) should typecheck
152              
153             String("str") will return {Type => ['$',String], Val =>"str"}
154              
155             MkVarDecl will return {Type => ['~',MkVarDecl,[],VarDecl,[]], Val => ...}
156             Just(Int(42)) will return {Type => ['|',Just,[{a => 'any'}],Maybe,[{a => 'any'}}, Val => {Type => ['$',Int], Val => 42}}
157            
158             To typecheck this against type Maybe(Int) will require checking the type of the Val
159             So maybe newtype must do this: if the typename or type ctor (yes, rather) has a variable then we need the actual type of the value
160              
161             =cut
162              
163             sub isprim {
164 0     0 0   ( my $tn ) = @_;
165             # say Dumper($tn);
166 0 0         if (ref($tn) eq 'ARRAY') {
167 0           $tn = $tn->[0];
168             }
169 0 0         if ( $tn =~ /^[a-z]|Bool|String|Int|Float|Double/ ) {
170 0           return 1;
171             } else {
172 0           return 0;
173             }
174             }
175             sub iscontainer {
176 0     0 0   (my $td) = @_;
177 0 0 0       if ( ref($td) eq 'Array' or
      0        
178             ref($td) eq 'Map' or
179             ref($td) eq 'Tuple'
180             ) {
181 0           return 1;
182             } else {
183 0           return 0;
184             }
185             }
186              
187             sub isprimcontainer {
188 0     0 0   (my $td) = @_;
189 0 0         iscontainer($td) && isprim($td->[2][0]);
190             }
191              
192             sub istypedval {
193 0     0 0   ( my $v ) = @_;
194             # say "istypedval: " . Dumper($v);
195              
196 0 0         if ( ref($v) eq 'Functional::Types' )
197             { # || (ref($v) eq 'HASH' and exists $v->{Type} and exists $v->{Val})) {
198 0           return 1;
199             } else {
200 0           return 0;
201             }
202             }
203              
204             =head1 TYPECHECKING
205              
206             This is a bit rough. We should maybe just check individual types, and always we must use the constructed type as a starting point, and the declared type to check against.
207             We typecheck in two different contexts:
208              
209             1/ Inside the newtype() call: for every element in @_, we should check against the arguments of the type constructor.
210             2/ Inside the bind() call: this call takes a typed value. For this typed value, all we really need to check is if its typename matches with the declared name.
211              
212             I think it might be better to have the same Type record structure for every type:
213              
214             Variant, Record: ['|~:', $ctor, [@ctor_args],$typename,[@typename_args]]
215              
216             Map, Tuple, Array: ['@%*', $ctor, [@ctor_args], $typename=$ctor,[]]
217              
218             Scalar: ['$', $ctor, [@ctor_args], $typename=$ctor,[]]
219              
220             =cut
221              
222             sub typecheck {
223             # local $VV=1;
224 0 0   0 0   say '%' x 80 if $VV;
225 0 0         say "TYPECHECK: " . Dumper(@_) if $VV;
226 0 0         say '%' x 80 if $VV;
227 0           ( my $t1, my $t2 ) = @_;
228 0 0         if (ref($t1) eq 'Functional::Types') {
229 0           $t1=$t1->{Type};
230             }
231 0 0         if (ref($t2) eq 'Functional::Types') {
232 0           $t2=$t2->{Type};
233             }
234 0 0         if (iscontainer($t1) ) {
235 0           my $tn1 = $t1->[1];
236 0 0         if (not iscontainer($t2) ) {
237 0           return (0,$tn1, $t2);
238             } else {
239 0           my $tn2 = $t2->[1];
240 0 0         if ($tn1 ne $tn2) {
241 0           return (($tn1 eq $tn2),$tn1, $tn2);
242             } else {
243             # Containers match, now check the enclosed type(s)
244 0           my $ctn1 = $t1->[2];
245 0           my $ctn2 = $t2->[2];
246 0           my $ii=0;
247 0           for my $et1 (@{$ctn1}) {
  0            
248 0           my $et2=$ctn2->[$ii++];
249 0           (my $st, my $ttn1, my $ttn2) = typecheck($et1, $et2);
250 0 0         if (!$st) {
251 0           return (0,$et1,$et2);
252             }
253             }
254 0           return (1,$tn1, $tn2);
255             }
256             }
257            
258             } else {
259             # At this point, we know the type is not a container.
260             # We can now test t1 to see if it is a Scalar, or bare string or an array containing a string
261             # say "REF:".ref($t1);
262             # say "REF:".ref($t2);
263 0 0         my $tn1= (ref($t1) eq 'ARRAY') ? $t1->[0] : (ref($t1) eq '' ? $t1 : (ref($t1) eq 'Scalar' ? $t1->[1] : $t1->[3]));
    0          
    0          
264 0 0         my $tn2= (ref($t2) eq 'ARRAY') ? $t2->[0] : (ref($t2) eq '' ? $t2 : (ref($t2) eq 'Scalar' ? $t2->[1] : $t2->[3]));
    0          
    0          
265 0 0 0       if ($tn1 =~/^[a-z]/ && $tn2!~/^[a-z]/) {
    0 0        
266 0           $tn1=$tn2;
267             } elsif ($tn2 =~/^[a-z]/ && $tn1!~/^[a-z]/) {
268 0           $tn2 = $tn1;
269             }
270 0           return (($tn1 eq $tn2),$tn1, $tn2);
271             }
272              
273             # # The actual type check
274             # my $tvarvals = {};
275             # for my $val ( @{$vals} ) {
276             # my $t = shift @{$tc_fields};
277             # if ( istypedval($t) ) {
278             # say "TYPEDVAL!";
279             # $t = $t->{Type};
280             # }
281             #
282             # # otherwise we compare field by field with $tc
283             # say 'VALTYPE:', Dumper($val);
284             # my $valtype = (
285             # istypedval($val)
286             # ? (
287             # ( ref( $val->{Type}[-1] ) eq 'ARRAY' )
288             # ? $val->{Type}[-2]
289             # : $val->{Type}[-1]
290             # )
291             # : $val
292             # ); # HACK!
293             # if ( istypedval($valtype) ) {
294             # say "TYPEDVAL!";
295             # $valtype = $valtype->{Type};
296             # }
297             # say 'VALTYPE2:', Dumper($valtype);
298             #
299             # if ( $valtype eq $t ) {
300             # say "TYPE CHECK OK!";
301             # } elsif ( $t =~ /^[a-z]$/ ) {
302             # say "TYPE CHECK: FOUND TYPE VAR $t, setting to $valtype";
303             #
304             # # $t=$valtype;
305             # $tvarvals->{$t} = $valtype;
306             # } elsif ( ref($t) eq 'ARRAY' and $t->[1] eq $valtype ) {
307             # say "TYPE CHECK AGAINST PRIM TYPE OK!";
308             # } else {
309             # die "TYPE CHECK NOK:", $valtype, "<>", Dumper($t);
310             # }
311             # }
312             # return $tvarvals;
313             }
314              
315             sub typecheck_prim {
316 0     0 0   say "PRIM:", Dumper(@_);
317              
318             # If it's a primitive type, there is no $tc, we compare with $t
319             # In this case the argument *must* be a scalar, so array ref rather than array
320 0           ( my $val, my $t ) = @_;
321 0           say "TYPE:", Dumper($t);
322 0           say "VAL:", Dumper($val);
323 0 0         if ( $t eq $val->{Type}[1] ) {
324 0           say "PRIM TYPE CHECK OK!";
325             } else {
326 0           die "PRIM TYPE CHECK NOK:", $val->{Type}[1], "<>", $t->[1];
327             }
328             }
329              
330             # So calls to primitive constructors don't ever return typed valies
331             sub newtype {
332 0     0 0   my @c = caller(1);
333 0 0         if($VV) {
334 0           say '=' x 80;
335 0           say "NEWTYPE Called from ", $c[3];
336 0           say "NEWTYPE ARGS:<<<";
337 0           say Dumper(@_);
338 0           say ">>>";
339             }
340 0 0 0       if ( scalar @_ == 1 and ref( $_[0] ) eq 'HASH' ) {
341             # This means we just got a value, should not happen I guess
342 0           die "Not enough arguments in call to newtype() :" . Dumper(@_);
343             } else {
344 0           my $t = shift @_;
345 0 0         say "TYPE:" . Dumper($t) if $VV;
346 0           my $arg = shift @_;
347 0 0         say "ARG:" . Dumper($arg) if $VV;
348 0 0         if ( ref($arg) =~ /OLD/ ) {
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
349              
350             # We need to treat the differnt types differntly I guess.
351             # There is a type constructor, get it
352 0           my $tc = shift;
353 0 0         say "TYPE CONSTRUCTOR:" . Dumper($tc) if $VV;
354 0           my @vals = @_;
355 0           my @tc_fields = @{ $tc->[2] };
  0            
356             # typecheck( \@vals, \@tc_fields );
357 0 0         if ( ref($t) ne 'ARRAY' ) {
358 0           say "WARNING: TYPE NAME NOT ARRAY for $t!";
359 0           $t = [ $t, [] ];
360             }
361              
362             # If the type check is OK, we combine the type constructor and the type name and the typename arguments
363             # Assuming $t = ['Typename',[@args]] and $tc=[ $kind, $ctor, [@ctor_arg_typenames]]
364             return
365 0           bless( { Type => [ @{$tc}, @{$t} ], Val => [@vals] }, 'Functional::Types' );
  0            
  0            
366             # ------------------------------------------------------------------------------------------------
367             } elsif ( ref($arg) eq 'Variant' ) {
368              
369             # newtype $t = Maybe(a), $tc = Variant(a), $v = @_
370 0           my $tc = $arg;
371             #bless( [
372             # '|',
373             # 'Just',
374             # [
375             # 'a'
376             # ]
377             # ], 'Variant' );
378 0 0         my $v = (@_>1)?[@_]:$_[0];
379            
380             ; # Assumption is that these are type value objects, but must check
381             # So I must compare $v against $tc->[1] I guess
382 0           my $ii=0;
383 0           for my $elt ( @_ ) {
384 0 0         say "ELT:".Dumper($elt) if $VV;
385 0           my $tc_tn = $tc->[2]->[$ii++];
386 0 0         say "TYPENAME:".Dumper($tc_tn) if $VV;
387 0 0         if ( ref($elt) eq 'Functional::Types' ) {
388 0           my $tn = $elt->{Type};#->[1];
389 0 0         if (defined $tn->[3]) { # HACK!
390 0           $tn= $tn->[3];
391             }
392 0 0         if ( not ($tc_tn=~/^[a-z]/) ) {
393             # say '#####'.Dumper($tn).'<>'.Dumper($tc_tn);
394 0           (my $st, my $t1, my $t2)=typecheck($tn, $tc_tn );
395 0 0         if( not $st ) {
396             # say '<',$tn->[3],'><',$tc_tn->[0],'>' if $VV;
397 0           die "Type error in Variant type check: $t1 <> $t2";
398             }
399             } else {
400 0           $tc_tn=$tn;
401             }
402             } else {
403 0 0         if ( !isprim( $tc_tn ) ) {
404 0           die
405             "Type error in Variant type check: $elt is not typed but $tc_tn is not a Primitive type.";#.Dumper($t);
406             }
407             }
408             }
409             # say Dumper($tc);
410 0           $tc=bless( [@{$tc},@{$t}], 'Variant');#die Dumper($tc);
  0            
  0            
411             #die 'HERE:'.Dumper($tc);
412             # What we should return is a typed value where the Val is a bare $v, and the Type is a Variant
413             # Now, U guess this is fine, but could we not have something like
414             # VarT a = Var1 a | Var2 Int String | Var3 (a,Bool) | Var4
415             # Then the type constructor would take several arguments so
416 0           return bless( { Type => $tc, Val => $v }, 'Functional::Types' );
417             # ------------------------------------------------------------------------------------------------
418             } elsif ( ref($arg) eq 'Record' ) {
419              
420             # - Record types: e.g. sub MkVarDecl { newtype $t=VarDecl, $tc=Record( ArgType, Int), $v=@_ }
421             # the Type field should become $tc,$t just as in Variant
422 0           my $tc = $arg;
423 0           my $v = [@_];
424             #say Dumper($tc,$v);
425             # bless( ['~','MkAlgType',[['String',[]],['Int',[]]]], 'Record' )
426             # ['GO!GO!',7188]
427 0           my $ii=0;
428 0           for my $elt ( @{$v} ) {
  0            
429 0 0         if ( ref($elt) eq 'Functional::Types' ) {
430 0           my $tn = $elt->{Type}->[1];
431 0           my $tc_tn = $tc->[2]->[$ii++];
432 0 0         if ( not typecheck( $tn, $tc_tn ) ) {
433 0           die "Type error in Record type check:";
434             }
435             } else { # bare value
436 0           my $tc_tn = $tc->[2]->[$ii++] ;
437 0 0         if ( !isprim( $tc_tn->[0] ) ) {
438 0           die
439             "Type error in Record type check: $tc is not a Primitive type";
440             }
441             }
442             }
443             # say Dumper($tc).Dumper($t);die;
444 0           $tc=bless( [@{$tc},@{$t}], 'Record');
  0            
  0            
445 0           return bless( { Type => $tc, Val => $v }, 'Functional::Types' );
446             # ------------------------------------------------------------------------------------------------
447             } elsif ( ref($arg) eq 'NamedRecord' ) {
448              
449             # - Record types: e.g. sub MkVarDecl { newtype $t=VarDecl, $tc=Record( acc1 => ArgType, acc2 => Int), $v=@_ }
450 0           my $tc = $arg;
451 0           my $v = [@_];
452 0           my $kvs = {};
453 0           my $ii=0;
454 0           for my $elt ( @{$v} ) {
  0            
455 0           my $tc_tf = $tc->[2]->[$ii++];
456 0           my $tc_tn = $tc->[2]->[$ii++];
457             # say 'TC:'.Dumper($tc_tn)."\nELT:".Dumper($elt->{Type});
458 0 0         if ( ref($elt) eq 'Functional::Types' ) {
    0          
459 0           my $tn = $elt->{Type}->[1];
460             # my $tc_tn = $tc->[2]->[$ii++];
461 0           (my $st, my $tn1, my $tn2) =typecheck( $tn, $tc_tn );
462 0 0         if ( not $st ) {
463 0           die "Type error in NamedRecord type check: $tn1, $tn2";
464             }
465            
466             } elsif ( !isprim( $tc->[0] ) ) {
467 0           die
468             "Type error in NamedRecord type check: $tc is not a Primitive type";
469             }
470 0           $kvs->{$tc_tf} = $elt;
471             }
472 0           $tc=bless( [@{$tc},@{$t}], 'NamedRecord');
  0            
  0            
473 0           return bless( { Type => $tc, Val => $kvs }, 'Functional::Types' );
474             # ------------------------------------------------------------------------------------------------
475             } elsif ( ref($arg) eq 'Function' ) {
476 0           die "FUNCTION NOT YET IMPLEMENTED!";
477             # ------------------------------------------------------------------------------------------------
478             } elsif ( ref($arg) eq 'Array' ) {
479              
480             # - Array type: sub IntList { newtype $tc = Array(Int), $v=@_ } is a primitive type
481             # This can only be used as IntList([T]) where in principle we should test *every* element of the list.
482 0           my $tc = $t; # ['Array',[Int]]
483 0           die Dumper($tc);
484             # What it should be is bless( ['@','Array',['Int'],'Array',[]],'Array'
485 0 0 0       my $v = (@_==1 && ref($_[0]) eq 'ARRAY') ? $_[0] : [@_];
486 0           my $elt_type= $tc->[1]->[0];
487 0 0         if (!isprim($elt_type)) {
488 0 0         if (not ($tc->[1] ~~ $v->[0]->{Type}->[1]) ) {
489 0           die "Type error in Array type check:";
490             }
491             }
492 0           return bless( { Type => $tc, Val => $v }, 'Functional::Types' );
493             # ------------------------------------------------------------------------------------------------
494             } elsif ( ref($arg) eq 'Map' ) {
495              
496             # - Map type: sub HashTable { newtype Map(String,Int), @_ } is a primitive type
497             # ------------------------------------------------------------------------------------------------
498             } elsif ( ref($arg) eq 'Tuple' ) {
499             # ------------------------------------------------------------------------------------------------
500             } elsif ( ref($arg) eq 'Scalar' ) {
501 0           die "TODO: SCALAR!";
502             # A scalar
503             # - Primitive types: e.g. sub ArgType { newtype String,@_ } # ArgType expects bare value. TODO: check it it's a typed value and untype
504              
505             } elsif ( ref($arg) eq 'HASH' ) {
506             # sub MyInt { newtype Scalar, @_ } and MyInt(Int(42))
507 0           die " GOT HASH, WHY?";
508 0           my $val = shift @_;
509 0           typecheck_prim( $val, $t );
510 0           return bless( { Type => $t, Val => $val }, 'Functional::Types' );
511             } else {
512 0 0         if (not defined $arg) {
513 0 0         say "TYPE ALIAS (only for scalar?)<".Dumper($t).','.Dumper(@_).'>' if $VV;
514 0 0         if ( isprim($t) ) {
515 0           return bless( { Type => bless([ '$', @{$t} ],'Scalar'), Val => $_[0] }, 'Functional::Types' );
  0            
516             } else {
517             # This is just pass-through
518 0           return $t;
519             }
520             } else {
521 0 0         say "TYPE ALIAS CALLED:".Dumper($t).','.Dumper($arg) if $VV;
522 0           my $tn = $t->[0];
523 0 0         if (isprim($tn) ) {
524 0           return bless( { Type => bless( [ '$', @{$t} ], 'Scalar'), Val => $arg }, 'Functional::Types' );
  0            
525             } else {
526 0           die "NO HANDLER FOR ".Dumper($t);
527             }
528             }
529              
530             }
531             }
532             } # END of newtype()
533              
534             =head1 BIND
535              
536             bind():
537            
538             bind $scalar, Int($v);
539             bind $list, SomeList($vs);
540             bind $map, SomeMap($kvs);
541             bind $rec, SomeRec(...);
542             bind $func, SomeFunc(...);
543              
544             For functions, bind() should do:
545              
546             - Take the arguments, which should be typed, typecheck them;
547             - call the original function with the typed args
548             - the return value should also be typed, just return it.
549              
550             So it might be very practical to have a typecheck() function
551              
552             Furthermore, we can do something similar to pattern matching by using a variant() function like this:
553              
554             given(variant $t) {
555             when (Just) : untype $t;
556             when (Nothing) :
557             }
558            
559             So variant() simply extracts the type constructor from a Variant type.
560              
561             =cut
562              
563             sub cast {
564 0     0 0   ( my $t, my $v ) = @_;
565 0           $t->{Val} = $v;
566             }
567              
568             # What should the complete type for a Variant be? Maybe, [ [Int,[]]], Just, []
569             sub bind {
570 0     0 0   ( my $t, my $tv, my @rest ) = @_;
571 0 0         if (@rest) {
572 0           $tv = [$tv,@rest];
573             }
574 0 0         say "BIND: T:<" . Dumper($t) . '>; V:<' . Dumper($tv) . '>' if $VV;
575              
576 0 0         if (istypedval($tv)) {
577 0           (my $st, my $t1, my $t2)=typecheck($t,$tv);
578 0 0         if (not $st) {
579 0           die "Typecheck failed in bind($t1,$t2)";
580             }
581             # We need the typenames from $t and from $tv
582 0           my $t_from_v = $tv->{Type}; # so [$k,...]
583 0 0         if (ref($t_from_v) eq 'Variant') {
    0          
    0          
    0          
584 0 0         if (ref($t->{Type}) eq 'Variant') {
585 0 0         if ($t_from_v->[3] eq $t->{Type}->[3]) {
586 0           $t->{Type}=$t_from_v;
587 0           } else { die "Type error in bind() for Variant";}
588             } else {
589 0 0         if ($t_from_v->[3] eq $t->{Type}->[0]) {
590 0           $t->{Type}=$t_from_v;
591 0           } else { die "Type error in bind() for Variant";}
592            
593             }
594             } elsif (ref($t_from_v) eq 'Record') {
595             # die Dumper($t_from_v);
596 0 0         if ($t_from_v->[3] eq $t->{Type}->[0]) {
597 0           $t->{Type}=$t_from_v;
598 0           } else { die "Type error in bind() for Record";}
599             } elsif (ref($t_from_v) eq 'NamedRecord') {
600             # die Dumper($t_from_v);
601 0 0         if ($t_from_v->[3] eq $t->{Type}->[0]) {
602 0           $t->{Type}=$t_from_v;
603 0           } else { die "Type error in bind() for NamedRecord";}
604              
605             } elsif (ref($t_from_v) eq 'Scalar') {
606 0           $t->{Type}=$t_from_v;
607             }
608             # $t is [$tn,[@targs]]
609 0           $t->{Val} = $tv->{Val};
610            
611             } else {
612             # must check if prim type for bare val
613             # say 'T:'. Dumper($t);
614 0 0         if (isprim($t->{Type}->[0])) {
    0          
615             # $t->{Type}->[3]
616             # $t->{Type}->[3]=ref($t->{Type});
617             # $t->{Type}->[4]=[];
618 0           $t->{Type}=bless(['$',@{$t->{Type}},@{$t->{Type}}],'Scalar');
  0            
  0            
619 0           $t->{Val} = $tv;
620            
621             } elsif (isprim($t->{Type}->[1])) {
622 0           $t->{Type}->[3]=ref($t->{Type});
623 0           $t->{Type}->[4]=[];
624 0           $t->{Val} = $tv;
625             } else {
626 0 0         if ( iscontainer($t->{Type}) ){
    0          
627 0 0         say '# FIXME: bind(): need to check the types of the elements of the container!' if $FIXME;
628 0           $t->{Type}->[3]=ref($t->{Type});
629 0           $t->{Type}->[4]=[];
630             # die Dumper($t->{Type});
631 0           $t->{Val} = $tv;
632             } elsif(ref($tv) eq 'CODE') {#die 'CODE';
633             # Function. We are assuming functions are typed but prim type args can be bare
634             # So we check the arguments when the function is call, if they are prim but not bare we make them that way
635             # Maybe do the same for containers holding prims
636 0     0     my $wrapper = sub { (my @args)=@_;
637 0           my $tt=$t;
638 0 0         if(ref($t) eq 'CODE') {
639 0           $tt=$t->();
640             }
641             # say 'QQ:'.Dumper($tt);
642 0 0         if (@args == 0) {
643 0           return $tt->{Type};
644             }
645            
646 0           my $ii=0;
647 0           for my $arg (@args) {
648 0           say 'ARG:'.Dumper($arg);
649 0           my $argtype=$tt->{Type}->[1]->[$ii++];
650 0 0         if (istypedval($arg)) {
    0          
651 0           say 'ARGTYPE:'.Dumper($argtype).Dumper($arg->{Type});
652 0           (my $st, my $t1, my $t2)=typecheck($argtype,$arg->{Type});
653 0 0         if (not $st) {
654 0           die "Typecheck failed in bind($t1,$t2)";
655             }
656 0 0         if (isprim($argtype)) {
    0          
657 0           $arg = untype $arg;
658             } elsif (isprimcontainer($argtype)) {
659 0           $arg = untype $arg;
660             }
661             } elsif (isprim($argtype)) {
662             # OK
663             } else {
664             # arg is bare
665 0           die "Type error: untyped arg, expecting $argtype!";
666             }
667            
668             }
669 0           my $retval = $tv->(@args);
670 0 0         if (ref($retval) ne 'Functional::Types') {
671 0           my $ret_type=$tt->{Type}->[1]->[-1];
672 0 0         if(ref($ret_type) eq 'ARRAY') {
673 0           $ret_type=$ret_type->[0];
674             }
675 0 0         if (isprim($ret_type) ) {
    0          
676 0           return bless({Type=>bless(['$',$ret_type,[],$ret_type,[]],'Scalar'),Val=>$retval},'Functional::Types')
677             } elsif( isprimcontainer($ret_type)) {
678 0           return bless( {Type=>$ret_type,Val=>$retval},'Functional::Types');
679             } else {
680             # type the return value
681 0           die 'RETVAL:'.Dumper($retval)."\nRETTYPE:".Dumper($ret_type);
682            
683 0           return eval("$ret_type($retval)");
684             }
685             } else {
686 0           return $retval;
687             }
688 0           };
689             # $t=$wrapper;
690 0           $_[0] = $wrapper;
691             } else {
692 0           die "TYPE NOT PRIM:".Dumper($t);
693             }
694             }
695             }
696             }
697              
698             # untype just recursively removes the type information
699             sub untype {
700 0     0 0   ( my $th ) = @_;
701 0 0         say "UNTYPE():".Dumper($th) if $VV;
702 0 0         if (ref($th) eq 'ARRAY') {
    0          
703 0           my @untyped_vals = ();
704 0           for my $elt ( @{$th} ) {
  0            
705 0           say "UNTYPE RECURSION IN ARRAY (TOP)\n";
706 0           die "SHOULD NOT HAPPEN!";
707 0           push @untyped_vals, untype($elt);
708             }
709 0           return [@untyped_vals];
710             } elsif (ref($th) eq 'Functional::Types') {
711 0           my $k = $th->{Type}[0];
712 0           my $val = $th->{Val};
713 0 0         if ( not defined $k ) {
714 0           die 'UNTYPE:' . Dumper($th);
715             }
716 0 0 0       if ( $k ne '@' and $k ne '$' and $k ne '%' and $k ne '*' ) { # NOT a scalar
    0 0        
      0        
717 0 0         if ( ref($val) eq 'ARRAY' ) {
    0          
    0          
718 0           my @untyped_vals = ();
719 0           for my $elt ( @{$val} ) {
  0            
720 0 0         say "UNTYPE RECURSION IN ARRAY\n" if $VV;
721 0           push @untyped_vals, untype($elt);
722             }
723 0           return [@untyped_vals];
724             } elsif ( ref($val) eq 'HASH' ) {
725              
726             # As this is not a scalar, it must be a record with named fields. Unless of course it is a typed value!
727 0 0         if ( istypedval($val) ) {
728              
729             # it's a typed value, just untype it
730 0 0         say "UNTYPE RECURSION IN HASH\n" if $VV;
731 0           return untype($val);
732             } else {
733 0           my $untyped_rec = {};
734 0           for my $k ( keys %{$val} ) {
  0            
735 0 0         say "UNTYPE RECURSION IN HASH VALUES\n" if $VV;
736 0           $untyped_rec->{$k} = untype( $val->{$k} );
737             }
738 0           return $untyped_rec;
739             }
740             } elsif ( ref($val) eq 'Functional::Types' ) {
741              
742             # This is basically the same as a typed value
743 0 0         say "UNTYPE RECURSION IN Types\n" if $VV;
744 0           return untype($val);
745             } else {
746              
747             # must be a scalar, just return it
748 0           return $val;
749             }
750             } elsif ( $k eq '&' ) {
751              
752             # a function
753 0           my $tf = $val->();
754 0           return $tf->{Val};
755             } else { # it must be a scalar
756 0           return $val; # AS-IS
757             }
758             } else {
759             # die "UNTYPE: NOT A REF: ".Dumper($th);
760 0           return $th;
761             }
762             } # END of untype()
763              
764              
765              
766             sub show_prim {
767 0     0 0   ( my $v, my $tn ) = @_;
768 0 0         if (ref($tn) eq 'ARRAY') {
769 0           $tn=$tn->[0];
770             }
771 0 0         if ( $tn eq 'String' ) {
    0          
772 0           return '"' . $v . '"';
773             } elsif ( $tn eq 'Bool' ) {
774 0 0         return ( $v ? 'True' : 'False' );
775             } else {
776 0           return $v;
777             }
778             }
779              
780             sub show {
781 0     0 0   ( my $tv ) = @_;
782             # local $VV=1;
783 0 0         say '=' x 80 if $VV;
784 0 0         say 'SHOW:'.Dumper($tv) if $VV;
785 0 0         if (ref($tv) eq 'Functional::Types') {
786 0           my $t = $tv->{Type};
787            
788             # my $k = $t->[0];
789             # This is the typename, so only a 'first guess', actual value depends on the prototype
790 0           my $tn = $t->[1]; # Note that this can actually be an array ref!
791 0           my $v = $tv->{Val};
792 0 0         if ( $t->isa('Scalar') ) {
    0          
    0          
    0          
    0          
    0          
    0          
    0          
793 0           return show_prim( $v, $tn );
794             } elsif ( $t->isa('Array' ) ) {
795 0           $tn = $t->[2]->[0];
796 0           my @s_vals = ();
797            
798             # Now, if the type is prim, I should just return it. Otherwise, first show() it
799             # so I need isprim as a check
800 0 0         if ( isprim($tn) ) {
801 0           for my $elt ( @{$v} ) {
  0            
802 0           push @s_vals, show_prim( $elt, $tn );
803             }
804             } else {
805 0           for my $elt ( @{$v} ) {
  0            
806 0           push @s_vals, show($elt);
807             }
808             }
809 0           my $sv_str = join( ', ', @s_vals );
810 0           return "[$sv_str]";
811              
812             } elsif ( $t->isa( 'Map' ) ) {
813 0           my $hvt = $t->[2][1];
814              
815             # we return a list of key-value pairs
816 0           my @kv_lst = ();
817 0 0         if ( isprim($hvt) ) {
818 0           for my $hk ( keys %{$v} ) {
  0            
819 0           my $hv = show_prim( $v->{$hk}, $hvt );
820 0           push @kv_lst, [ $hk, $hv ];
821             }
822             } else {
823              
824             # first show the values
825 0           for my $hk ( keys %{$v} ) {
  0            
826 0           my $hv = show( $v->{$hk} );
827 0           push @kv_lst, [ $hk, $hv ];
828             }
829             }
830 0           my @kv_str_lst = map { '("' . $_->[0] . '", ' . $_->[1] . ')' } @kv_lst;
  0            
831 0           my $kv_lst_str = join( ', ', @kv_str_lst );
832 0           return 'fromList [' . $kv_lst_str . ']';
833              
834             } elsif ( $t->isa( 'Tuple' ) ) { # Tuple type
835 0           $tn=$t->[2];
836 0           my @tns = @{$tn};
  0            
837 0           my @s_vals = ();
838 0           my $ii=0;
839 0           for my $et (@tns) {
840 0           my $ev = $v->[$ii++];
841 0           say 'E:'.Dumper($et).isprim($et);
842 0 0         my $sv = isprim($et) ? show_prim( $ev, $et ) : do { say 'HERE'.Dumper($et);show($ev)};
  0            
  0            
843 0           push @s_vals, $sv;
844             }
845 0           return '(' . join( ', ', @s_vals ) . ')';
846             } elsif ( $t->isa( 'Record' ) ) {
847             # die Dumper($t);
848 0           my $ctor = $t->[1];
849 0           my @tns = @{ $t->[2] };
  0            
850 0           my @s_vals = ();
851 0           my $ii=0;
852 0           for my $et (@tns) {
853 0           my $ev = $v->[$ii++];
854 0 0         my $sv = isprim($et) ? show_prim( $ev, $et ) : show($ev);
855 0 0         if ( $sv =~ /\s/ ) { $sv = "($sv)" }
  0            
856 0           push @s_vals, $sv;
857             }
858 0           my $svret= $ctor . ' ' . join( ' ', @s_vals );
859             # say Dumper($svret);
860 0           return $svret;
861             } elsif ( $t->isa( 'NamedRecord' ) ) { #say 'SHOW NAMEDREC: T:'. Dumper($t)."\nV:".Dumper($v);
862 0           my $ctor = $tn;
863 0           my @tns = @{ $t->[2] };
  0            
864 0           my @s_vals = ();
865 0           my $idx = 0;
866             # my $ii=0;
867 0           while ( $idx < @tns )
868             { # Note that the first elt is the field name! Maybe I should encode them as arefs
869 0           my $fn = $tns[$idx];
870 0           my $ft = $tns[ $idx + 1 ];
871 0           $idx += 2;
872 0           my $ev = $v->{$fn};
873 0 0         my $sv = isprim($ft) ? show_prim( $ev, $ft ) : show($ev);
874 0 0         if ( $sv =~ /\s/ ) { $sv = "($sv)" }
  0            
875 0           push @s_vals, "$fn = $sv";
876             }
877 0           return $ctor . ' {' . join( ', ', @s_vals ) . '}';
878             } elsif ( $t->isa( 'Variant' ) ) {
879 0           my $ctor = $tn; # This is fine.
880             # A Variant will always take a typed value, so we just show that
881 0 0         if (defined $v) {
882 0 0 0       if (ref($v) eq 'ARRAY' and @{$v}) {
  0 0          
883 0           return '('.$ctor . ' ' . join(' ',map {show($_)} @{$v}).')';
  0            
  0            
884             } elsif (ref($v) eq 'Functional::Types') {
885 0           $tn.' '.show($v);
886             } else {
887 0           die 'NOT ARRAY:'.Dumper($v);
888             }
889             } else {
890 0           return $ctor;
891             }
892             } elsif ( $t->isa( 'Function' ) ) {
893 0           die "It is not possible to show() a function\n";
894             } else {
895 0           die "Unknown kind ".ref($t)."\n";
896             }
897             } else {
898 0           return $tv;
899             }
900             } # END of show()
901              
902             sub read {
903 0     0 0   say Dumper(@_);
904 0           my $res = eval($_[0]);
905 0           return $res;
906             }
907              
908             sub match {
909 0     0 0   (my $tv)=@_;
910 0 0         if (ref($tv->{Val}) eq 'ARRAY') {
911 0           return @{$tv->{Val}}
  0            
912             } else {
913 0           return $tv->{Val};
914             }
915             }
916              
917             ################################################################################
918             # PROTOTYPES
919             ################################################################################
920              
921             =head1 PROTOTYPES
922              
923             * These are *not* to be called directly, only as part of a newtype call, unless you know what you're doing.
924              
925             * I realise it would be faster for sure to have numeric codes rather than strings for the different prototypes.
926              
927             The prototype call returns information on the kind of type, the type constructor and the arguments. Currently:
928              
929             * PRIM, storing untyped values:
930              
931             Scalar: ['$', $type], Val = $x => NEVER used as-is
932            
933             Array: ['@', $type], Val = [@xs]
934             Hash: ['%', [$ktype,$vtype]], Val = {@kvpairs}
935             Tuple: ['*', [@tupletypes]], Val = [@ts]
936              
937             * PROPER, storing only typed values:
938              
939             Variant: ['|', $ctor, [@ctor_args],$typename,[@typename_args]], Val = ???
940             Record: ['~', $ctor, [@ctor_args],$typename,[@typename_args]], Val = ???
941             Record with fields: [':', $ctor, [@ctor_args_fields],$typename,[@typename_args]] , Val = {}
942              
943             * FUNCTION, the function can itself take typed values or untyped ones, depending on cast() or bind()
944             What we store is actually a wrapper around the function, to deal with the types
945             So we should somehow get the original function back. I think we can do this by calling the wrapper without any arguments,
946             in which case it should return a typed value with the function's type in Type and the original function in Value
947             Anyhow untype() only makes sense for a function that works on untyped values of course
948            
949             Function: ['&',[@function_arg_types]], Val = \&f
950              
951             In a call to type() the argument will only return [$typename,[@typename_args]]
952             For a scalar type I could just return $typename but maybe consistency?
953              
954             In a newtype call, the primitive types don't have a constructor.
955             There is some asymmetry in the '$' type compared to the others:
956              
957             Normally the pattern is Prototype($typename) but for primitive types it is just Scalar() and the prim type's typename comes from caller()
958              
959             Also, prim types are created without newtype(), I think I should hide this behaviour.
960              
961             Maybe I need to distinguish between a new data and a type alias, it would certainly clarify things;
962             Also, I guess for a type alias for a prim type we can feed it an untyped value.
963            
964             =cut
965              
966             sub Scalar {
967 0     0 0   my @c = caller(1);
968 0           my $t = $c[3];
969 0           $t =~ s/^.+:://;
970 0 0         if (@_) {
971 0           my $v = $_[0];
972 0 0         if ( istypedval($v) ) {
973 0           die 'Scalar:' . Dumper($v);
974 0           untype($v);
975             }
976             return
977 0           bless( { Val => $v, Type => bless( [ '$', $t, [], $t, [] ], 'Scalar' ) },
978             'Functional::Types' );
979             } else {
980             return
981 0           [$t,[]];
982             # bless(['$',$t,[]],'Scalar');
983             ; # Scalar should never be called without args except in a newtype() context.
984             }
985             }
986              
987              
988             # What the Record() call does is create a type representation for the constructor. We need to complement this with the typename, newtype() should do that.
989             # For that reason, the typename should maybe be the last argument, we simply append it to the list. I don't think we need the '[', instead I will use ':' for named fields.
990              
991             # --------------------------------------------------------------------------
992             sub Record {
993 0     0 0   my @c = caller(1);
994 0           my $type_constructor = $c[3];
995 0           $type_constructor =~ s/^.+:://;
996 0 0         my $kind =
997             $_[0] =~ /^[a-z_]/
998             ? ':'
999             : '~'
1000             ; # oblique way of saying that this record has named fields. newtype() should use this to create a hash for the values.
1001 0 0         my $maybe_named = ( $kind eq '~' ) ? '' : 'Named';
1002 0           my $type_representation =
1003             bless( [ $kind, $type_constructor, [@_] ], $maybe_named . 'Record' );
1004             # say 'RECORD:'.Dumper($type_representation);
1005 0           return $type_representation;
1006             }
1007              
1008             sub field {
1009 0     0 0   (my $r, my $fn, my $v) = @_;
1010              
1011 0 0         if (defined $v) {
1012 0           $r->{Val}->{$fn}=$v;
1013             } else {
1014 0           return $r->{Val}->{$fn};
1015             }
1016              
1017             }
1018             # --------------------------------------------------------------------------
1019             sub Variant {
1020 0     0 0   my @c = caller(1);
1021 0           my $tc = $c[3];
1022 0           $tc =~ s/^.+:://;
1023 0 0         say "Variant: TYPENAME: $tc" if $VV;
1024 0 0         say "Variant: TYPE ARGS: ", Dumper(@_) if $VV;
1025 0           my $type_representation = bless( [ '|', $tc, [@_] ], 'Variant' );
1026 0           return $type_representation;
1027             }
1028             # Given a typed value object, and assuming it is a Variant, return the type constuctor
1029             sub variant {
1030 0     0 0   (my $tv) = @_;
1031             # say Dumper($tv->{Type});
1032 0           return $tv->{Type}->[1];
1033             }
1034             # sub IntList { newtype Array(Int),@_ }
1035             # type my $int_lst => Array(Int)
1036             # let $int_lst, $untyped_lst;
1037             # OR
1038             # NO: let $int_lst, Array(@untyped_lst); # This is direct use of a prototype, NOT GOOD!
1039             # OR
1040             # my $int_lst = IntList(@untyped_lst);
1041             # --------------------------------------------------------------------------
1042             sub Array { # Array Ctor only ever takes a single argument
1043 0     0 0   my @c = caller(1);
1044 0   0       my $arg = $_[0] // [];
1045 0 0         say "Array: TYPE ARGS: ", Dumper(@_) if $VV;
1046 0           my $tc = 'Array';
1047 0 0         if (@c) {
1048 0           my $tc = $c[3];
1049 0           $tc =~ s/^.+:://;
1050 0           say "Array: TYPENAME: $tc";
1051             }
1052 0           my $type_representation = bless( [ '@', $tc, [$arg] ], 'Array' );
1053 0           return $type_representation;
1054             }
1055             # 'at' for array indexing
1056             sub at {
1057 0     0 0   (my $a, my $idx, my $v) =@_;
1058 0 0         if (defined $v) { say '# FIXME: $v could be an untyped value!' if $FIXME;
  0 0          
1059 0 0         if (ref($v) eq 'Functional::Types') {
1060 0 0         if (typecheck($v->{Type}, $a->{Type})) {
1061 0           $a->{Val}->[$idx] = $v->{Val};
1062             } else {
1063 0           die "Type error: ::at(".$v->{Type}[1] .") ";
1064             }
1065             } else {
1066 0 0         say '# FIXME: at() must check if the corresponding type is primitive!' if $FIXME;
1067 0           $a->{Val}->[$idx] = $v;
1068             }
1069             } else {
1070 0           return $a->{Val}->[$idx];
1071             }
1072             }
1073             sub length {
1074 0     0 0   (my $a)=@_;
1075 0           return scalar @{$a->{Val}};
  0            
1076             }
1077             sub push {
1078 0 0   0 0   say "PUSH:". Dumper(@_) if $VV;
1079 0           (my $a, my $v) =@_;
1080 0           push @{ $a->{Val} }, $v;
  0            
1081             # die "Type error: Array::push(".$v->{Type}[1] .") <> ";
1082             }
1083             sub pop {
1084 0     0 0   (my $a)=@_;
1085 0           return {'Val' => pop( @{$a->{Val}}), 'Type' => $a->{Type}->[2]};
  0            
1086             }
1087             sub shift {
1088 0     0 0   (my $a)=@_;
1089 0           return {'Val' => shift( @{$a->{Val}}), 'Type' => $a->{Type}->[2]};
  0            
1090             }
1091             sub unshift {
1092 0     0 0   (my $a, my $v) =@_;
1093 0 0         if ($v->{Type} ~~ $a->{Type}[2]) {
1094              
1095 0           unshift @{$a->{Val}}, $v->{Val};
  0            
1096             } else {
1097 0           die "Type error: Array::unshift(".$v->{Type}[1] .") ";
1098             }
1099              
1100             }
1101              
1102             sub elts {
1103 0     0 0   (my $a)=@_;
1104 0           return @{ $a->{Val} };
  0            
1105             }
1106             # --------------------------------------------------------------------------
1107              
1108              
1109             # The main question is always, should constructors with primitive types take typed values or bare values?
1110             #
1111             # If I have a Map(String,Int) I think it is more intuitive to accept bare values. In this case, the underlying hash will store bare keys and values.
1112             # If I have a Map(String,ArgRec) then the underlying hash will store typed values but bare keys.
1113             # The problem is what to return:
1114             #
1115             # my $v = $h->of ($k);
1116             #
1117             # So, should $v be bare or typed? My feeling is that it should be typed.
1118             # But in case of
1119             #
1120             # $h->of($k,$v);
1121             #
1122             # I think $v could be untyped.
1123             #
1124             # Which means that if $v is of a primitive type I should construct a typed value and return it. Does that make sense? Because we could always allow primitive values to be handled untyped.
1125             # In that case returning them untyped is better!
1126             #
1127              
1128              
1129             # sub Hash { newtype Map(String,T2),@_ }
1130             sub Map {
1131 0     0 0   my @c = caller(1);
1132 0           my $tc = 'Map';
1133 0 0         if (@c) {
1134 0           $tc = $c[3];
1135 0           $tc =~ s/^.+:://;
1136 0           say "Map: TYPENAME: $tc";
1137 0           say "Map: TYPE ARGS: ", Dumper(@_);
1138             }
1139 0           my $type_representation = bless( [ '%', $tc, [@_] ], 'Map' );
1140 0           return $type_representation;
1141             }
1142              
1143             sub insert {
1144 0     0 0   ( my $h, my $k, my $v ) = @_;
1145 0           $h->{Val}{$k} = $v;
1146             } # but we could use 'of' with two arguments
1147              
1148             sub of {
1149 0     0 0   ( my $h, my $k, my $v ) = @_;
1150 0 0         if ( defined $v ) {
1151 0 0         say '# FIXME
1152             # To be correct, we need to unbox typed values
1153             # But I think I will assume $k is always a bare string and $v is stored as-is' if $FIXME;
1154 0           $h->{Val}{$k} = $v;
1155             } else {
1156             # say 'h->of():' . Dumper($h);
1157             # say 'of(k):' . Dumper($k);
1158 0           my $kv = $k;
1159 0 0         if ( istypedval($k) ) {
1160 0           $kv = $k->{Val};
1161             }
1162 0           my $retval = $h->{Val}{$kv};
1163             # say Dumper($retval);
1164 0           return $retval;
1165             }
1166             }
1167              
1168             sub has {
1169 0     0 0   ( my $h, my $k ) = @_;
1170 0           return exists $h->{Val}{ $k->{Val} };
1171             } # exists
1172              
1173             sub keys {
1174 0     0 0   ( my $h ) = @_;
1175              
1176             #return ( map { { 'Val' => $_, 'Type' => $h->{Type}->[2] } }
1177 0           return keys( %{ $h->{Val} } );
  0            
1178              
1179             }
1180              
1181             sub size {
1182 0     0 0   ( my $h ) = @_;
1183 0           return scalar( CORE::keys( %{ $h->{Val} } ) );
  0            
1184             }
1185              
1186             # --------------------------------------------------------------------------
1187             # sub ArgTup { newtype Tuple(T1,T2,T3),@_ }
1188             sub Tuple {
1189 0     0 0   my @c = caller(1);
1190 0           my $tc = 'Tuple';
1191 0 0         if (@c) {
1192 0           $tc = $c[3];
1193 0           $tc =~ s/^.+:://;
1194 0           say "Tuple: TYPENAME: $tc";
1195 0           say "Tuple: TYPE ARGS: ", Dumper(@_);
1196             }
1197 0           my $type_representation = bless( [ '*', $tc, [@_] ], 'Tuple' );
1198 0           return $type_representation;
1199              
1200             }
1201              
1202             # For function types, if we do e.g.
1203             # sub MkParser {newtype Parser(a),Function(String => Tuple(Maybe a, String)),@_}
1204             sub Function {
1205 0     0 0   my @c = caller(1);
1206 0           my $tc = $c[3];
1207 0           $tc =~ s/^.+:://;
1208 0           my $type_representation = bless( [ '&', $tc, [@_] ], 'Function' );
1209 0           return $type_representation;
1210             }
1211             ################################################################################
1212              
1213 0     0 0   sub Int (;$) { Scalar(@_) }
1214 0     0 0   sub String (;$) { Scalar(@_) }
1215 0     0 0   sub Float (;$) { Scalar(@_) }
1216 0     0 0   sub Double (;$) { Scalar(@_) }
1217              
1218             # Bool should evaluate its arg and return a type { Type => ['$','Bool'], Val => 1 or 0 }
1219             sub Bool {
1220 0 0   0 0   if (@_) {
1221 0 0         my $b = $_[0] ? 1 : 0;
1222 0           Scalar($b);
1223             } else {
1224 0           Scalar();
1225             }
1226             }
1227             sub True {
1228 0     0 0   bless( { Type => bless(['$','True',[],'Bool',[]], 'Scalar'), Val => 1 }, 'Functional::Types');
1229             }
1230             sub False {
1231 0     0 0   bless( { Type => bless(['$','False',[],'Bool',[]], 'Scalar'), Val => 0 }, 'Functional::Types');
1232             }
1233             ################################################################################
1234              
1235             1;