Perl / perl5

đŸȘ The Perl programming language
https://dev.perl.org/perl5/
Other
1.91k stars 542 forks source link

smartmatch signatures #16260

Open p5pRT opened 6 years ago

p5pRT commented 6 years ago

Migrated from rt.perl.org#132473 (status was 'open')

Searchable as RT132473$

p5pRT commented 6 years ago

From zefram@fysh.org

Created by zefram@fysh.org

Everyone would like to have type constraints in signatures\, but a prohibitive issue is that the core doesn't have a general type system. (There are classes\, but they don't address any of the type distinctions we frequently need to make between unblessed data types.) Smartmatch could become the core's type system\, as we've discussed a bit when talking about the future of smartmatch.

Suppose that the smartmatch/switch discussions lead to a version of smartmatch that we can all live with. (In the last round of discussion we seemed to be agreed on smartmatch semantics\, and it's just the given/when stuff that's holding us back.) This would be almost entirely based on explicit overloading of the smartmatch operator by blessed objects that appear on the rhs of smartmatch. We would expect to see a rich ecosystem of matcher object classes that implement all kinds of predicate\, including composite predicates that incorporate subordinate matcher objects.

Matcher objects can then be used as type metaobjects. In general they're types in only the minimal sense that they have a concept of type membership. Specific kinds of matcher object may also have other kinds of type-related behaviour; for example\, Moose class metaobjects would presumably overload smartmatch to implement a class membership check\, while also having introspective methods that only make sense for the Moose concept of a class. But we don't need to worry about that; for signatures the only aspect of a type that we care about is the membership predicate.

So a scalar signature item could specify a type constraint by referring to an appropriate matcher object. We'd want to permit an arbitrary expression for the matcher object\, to allow for parameterised and composite types. So very little signature syntax is required; really just a signifier for smartmatching and a grammatical slot into which to put the matcher expression. Using signature syntax otherwise as it is today\, this would look something like​:

  use Smart_Type qw(Num Str union HashRef_of); # imaginary module   use feature "signatures";   sub gronk ($why ~Str\, $how_hard ~Num = 5) {...}   sub annotate ($subject\, $msg ~ union(Str\, HashRef_of(Str))) {...}   sub nom ($food ~ My​::Food->meta = My​::Apple->new) {...}

"~" references the smartmatch operator while not being longer than necessary. The matcher expression would have to be restricted to expressions above some middling precedence level\, so that in the case of an optional parameter it doesn't read the "=" and default value expression as part of the matcher expression. But I imagine matcher objects would also overload the bitwise operators to perform Boolean compositions\, so it would be nice for the threshold to be lower than those operators. Any expression of lower precedence than the threshold could of course be parenthesised. So we'd have things like​:

  use Smart_Type qw(Str HashRef_of);   use feature "signatures";   sub annotate ($subject\, $msg ~ Str | HashRef_of(Str)) {...}   sub frob ($spec ~ (state $fspec_type = gen_fspec_type())) {...}

With respect to the last of those examples\, usually the type we want to smartmatch against is fixed for all time\, and recomputing the matcher for each execution of the sub would be unwanted expense\, potentially significant. So making the matcher be implicitly saved in an invisible state variable might be a better way to go. However\, it's impossible to get fully dynamic type constraint logic (referring to earlier parameters) if that's always done\, so consideration should then be given to having a variant of "~" that explicitly makes the matcher expression dynamic.

In optional parameters I imagine the default value expression would be exempt from the type constraint\, but this could go either way.

It's a little more difficult to apply type constraints to array and hash parameters. We probably want to apply the type constraint to the whole array or hash\, for maximum flexibility\, but the actual smartmatch operand would have to be *a reference to* the array or hash\, so how do we indicate that in the signature syntax?

It's also necessary to think about how type constraints would interact with aliasing in signatures [perl #132472].

Perl Info ``` Flags: category=core severity=wishlist Site configuration information for perl 5.27.5: Configured by zefram at Fri Oct 20 23:24:00 BST 2017. Summary of my perl5 (revision 5 version 27 subversion 5) configuration: Platform: osname=linux osvers=3.16.0-4-amd64 archname=x86_64-linux-thread-multi uname='linux barba.rous.org 3.16.0-4-amd64 #1 smp debian 3.16.43-2+deb8u2 (2017-06-26) x86_64 gnulinux ' config_args='-des -Dprefix=/home/zefram/usr/perl/perl_install/perl-5.27.5-i64-f52 -Duselargefiles -Dusethreads -Uafs -Ud_csh -Uusesfio -Uusenm -Duseshrplib -Dusedevel -Uversiononly -Ui_db' hint=recommended useposix=true d_sigaction=define useithreads=define usemultiplicity=define use64bitint=define use64bitall=define uselongdouble=undef usemymalloc=n default_inc_excludes_dot=define bincompat5005=undef Compiler: cc='cc' ccflags ='-D_REENTRANT -D_GNU_SOURCE -fwrapv -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -D_FORTIFY_SOURCE=2' optimize='-O2' cppflags='-D_REENTRANT -D_GNU_SOURCE -fwrapv -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include' ccversion='' gccversion='4.9.2' gccosandvers='' intsize=4 longsize=8 ptrsize=8 doublesize=8 byteorder=12345678 doublekind=3 d_longlong=define longlongsize=8 d_longdbl=define longdblsize=16 longdblkind=3 ivtype='long' ivsize=8 nvtype='double' nvsize=8 Off_t='off_t' lseeksize=8 alignbytes=8 prototype=define Linker and Libraries: ld='cc' ldflags =' -fstack-protector-strong -L/usr/local/lib' libpth=/usr/local/lib /usr/lib/gcc/x86_64-linux-gnu/4.9/include-fixed /usr/include/x86_64-linux-gnu /usr/lib /lib/x86_64-linux-gnu /lib/../lib /usr/lib/x86_64-linux-gnu /usr/lib/../lib /lib libs=-lpthread -lnsl -ldb -ldl -lm -lcrypt -lutil -lc perllibs=-lpthread -lnsl -ldl -lm -lcrypt -lutil -lc libc=libc-2.19.so so=so useshrplib=true libperl=libperl.so gnulibc_version='2.19' Dynamic Linking: dlsrc=dl_dlopen.xs dlext=so d_dlsymun=undef ccdlflags='-Wl,-E -Wl,-rpath,/home/zefram/usr/perl/perl_install/perl-5.27.5-i64-f52/lib/5.27.5/x86_64-linux-thread-multi/CORE' cccdlflags='-fPIC' lddlflags='-shared -O2 -L/usr/local/lib -fstack-protector-strong' @INC for perl 5.27.5: /home/zefram/usr/perl/perl_install/perl-5.27.5-i64-f52/lib/site_perl/5.27.5/x86_64-linux-thread-multi /home/zefram/usr/perl/perl_install/perl-5.27.5-i64-f52/lib/site_perl/5.27.5 /home/zefram/usr/perl/perl_install/perl-5.27.5-i64-f52/lib/5.27.5/x86_64-linux-thread-multi /home/zefram/usr/perl/perl_install/perl-5.27.5-i64-f52/lib/5.27.5 Environment for perl 5.27.5: HOME=/home/zefram LANG (unset) LANGUAGE (unset) LD_LIBRARY_PATH (unset) LOGDIR (unset) PATH=/home/zefram/usr/perl/perl_install/perl-5.27.5-i64-f52/bin:/home/zefram/usr/perl/util:/home/zefram/pub/x86_64-unknown-linux-gnu/bin:/home/zefram/pub/common/bin:/usr/bin:/bin:/usr/local/bin:/usr/games PERL_BADLANG (unset) SHELL=/usr/bin/zsh ```
p5pRT commented 6 years ago

From @cpansprout

On Mon\, 20 Nov 2017 06​:59​:19 -0800\, zefram@​fysh.org wrote​:

Everyone would like to have type constraints in signatures\, but a prohibitive issue is that the core doesn't have a general type system. (There are classes\, but they don't address any of the type distinctions we frequently need to make between unblessed data types.) Smartmatch could become the core's type system\, as we've discussed a bit when talking about the future of smartmatch.

Suppose that the smartmatch/switch discussions lead to a version of smartmatch that we can all live with. (In the last round of discussion we seemed to be agreed on smartmatch semantics\, and it's just the given/when stuff that's holding us back.) This would be almost entirely based on explicit overloading of the smartmatch operator by blessed objects that appear on the rhs of smartmatch. We would expect to see a rich ecosystem of matcher object classes that implement all kinds of predicate\, including composite predicates that incorporate subordinate matcher objects.

Matcher objects can then be used as type metaobjects. In general they're types in only the minimal sense that they have a concept of type membership. Specific kinds of matcher object may also have other kinds of type-related behaviour; for example\, Moose class metaobjects would presumably overload smartmatch to implement a class membership check\, while also having introspective methods that only make sense for the Moose concept of a class. But we don't need to worry about that; for signatures the only aspect of a type that we care about is the membership predicate.

So a scalar signature item could specify a type constraint by referring to an appropriate matcher object. We'd want to permit an arbitrary expression for the matcher object\, to allow for parameterised and composite types. So very little signature syntax is required; really just a signifier for smartmatching and a grammatical slot into which to put the matcher expression. Using signature syntax otherwise as it is today\, this would look something like​:

use Smart_Type qw(Num Str union HashRef_of); # imaginary module

Those would be subs (maybe constants)\, right?

use feature "signatures"; sub gronk ($why ~Str\, $how_hard ~Num = 5) {...} sub annotate ($subject\, $msg ~ union(Str\, HashRef_of(Str))) {...} sub nom ($food ~ My​::Food->meta = My​::Apple->new) {...}

"~" references the smartmatch operator while not being longer than necessary. The matcher expression would have to be restricted to expressions above some middling precedence level\, so that in the case of an optional parameter it doesn't read the "=" and default value expression as part of the matcher expression. But I imagine matcher objects would also overload the bitwise operators to perform Boolean compositions\, so it would be nice for the threshold to be lower than those operators. Any expression of lower precedence than the threshold could of course be parenthesised.

?​: is also immensely useful. Make ?​: the lowest precedence permitted.

So we'd have things like​:

use Smart_Type qw(Str HashRef_of); use feature "signatures"; sub annotate ($subject\, $msg ~ Str | HashRef_of(Str)) {...} sub frob ($spec ~ (state $fspec_type = gen_fspec_type())) {...}

With respect to the last of those examples\, usually the type we want to smartmatch against is fixed for all time\, and recomputing the matcher for each execution of the sub would be unwanted expense\, potentially significant. So making the matcher be implicitly saved in an invisible state variable might be a better way to go. However\, it's impossible to get fully dynamic type constraint logic (referring to earlier parameters) if that's always done\, so consideration should then be given to having a variant of "~" that explicitly makes the matcher expression dynamic.

Or the other way round.

So it’s a matter of arguing about the syntax. Finding something that conveys permanence without confusion with other operators is hard. My first thought was = to imply state variable assignment\, but then you have ~=\, which means something completely different elsewhere.

Since the syntax before the ~ is not restricted so much\, maybe​:

  sub fronk ($foo state~predicate())

Unambiguous\, but confusing!

I think the indicator\, whatever it is\, ought to be before the ~\, since putting it after the ~ limits the syntax\, unless we start requiring whitespace after the ~.

I will trying brainstorming with every symbol of my keyboard​:

`~ -- confusing !~ -- confusing @​~ -- confusing #~ -- very confusing $~ -- confusing %~ -- confusing ^~ -- ‘pin’ the predicate to a state var &~ -- confusing *~ -- Perl 6’s ‘whatever’ star; use ‘whatever’ the predicate expression produces each time (dynamic) (~ -- confusing (same with {}[]) )~ -- ambiguous '~ -- confusing "~ -- confusing \<~ -- unambiguous\, but couldn’t say whether that means dynamic or fixed

~ -- same \,~ -- ambiguous .~ -- may ‘pin’ the predicate\, as with ^= ?~ -- conflicts with other proposed syntax /~ -- usable\, like \<~ =~ -- confusing +~ -- usable \~ -- conflicts with other proposed syntax |~ -- Makes me think of bitwise ops -~ -- usable _~ -- usable\, but requires whitespace​: previous predicate is ‘under’stood ;~ -- confusing; looks like a multidimensional syntax :\~ -- usable ⌘~ -- I did say I was using every symbol on the keyboard. :-)

So the only options that I do not find objectionable are​:

If we want fixed predicates to be the default​:

*~ \<~

~ /~ +~ -~ :\~

If we want dynamic predicates to be the default​:

^~ \<~

~ .~ /~ +~ -~ _~ :\~

If we go with that last one\, we will have people deliberately parenthesizing their predicates\, just to make funny faces​:

sub fronk (@​a :\~( Str )) {...}   ^^^

In optional parameters I imagine the default value expression would be exempt from the type constraint\, but this could go either way.

Skipping the constraint would be faster. That would assume the programmer knows what he is doing\, which is not an unreasonable assumption.

It's a little more difficult to apply type constraints to array and hash parameters. We probably want to apply the type constraint to the whole array or hash\, for maximum flexibility\, but the actual smartmatch operand would have to be *a reference to* the array or hash\, so how do we indicate that in the signature syntax?

Don’t indicate it; just document it as the behaviour. People can write array-specific predicates.

  sub foo (@​a~Str) # will probably fail; \@​a is not a Str   sub foo (@​a~Array_of_Str)   sub foo (@​a~HasEvenNumberOfElements)

It's also necessary to think about how type constraints would interact with aliasing in signatures [perl #132472].

In terms of what the smartmatcher sees\, I think the only reasonable behaviour is to do the aliasing first\, and then apply the type constraint to the lexical variable created by the parameter.

So (\$x~Str) will effectively do (\$_[0] ~~ Str) and ($x\~Str) will effectively do ( $_[0] ~~ Str)

In the former case\, $_[0] would not necessarily be a ref\, but in the latter case it would have to be.

There is still the question of what order the \ and ~ come​:

  ($x\~Str) and ($x\~Str=default)   ($x~Str\) and ($x~Str\=default)

The former would be much easier to parse.

--

Father Chrysostomos

p5pRT commented 6 years ago

The RT System itself - Status changed from 'new' to 'open'

p5pRT commented 6 years ago

From zefram@fysh.org

Father Chrysostomos via RT wrote​:

On Mon\, 20 Nov 2017 06​:59​:19 -0800\, zefram@​fysh.org wrote​:

use Smart_Type qw(Num Str union HashRef_of); # imaginary module

Those would be subs (maybe constants)\, right?

Yes. All are subs. Num and Str are constant-valued nullary subs\, presumably with () prototypes and the usual machinery to constant-fold. The other two take parameters (union() is variadic and HashRef_of() is unary)\, so can't be ordinary constants\, but they're pure functions and so could potentially constant-fold by means of a special call checker. Apart from that optional call checker\, and optional XS implementation for performance\, the Smart_Type module could be easily written in pure Perl\, and would export the subs in the ordinary way.

?​: is also immensely useful. Make ?​: the lowest precedence permitted.

That's the precedence level immediately above assignment\, so that just fits. We should add a parse_*expr() function for that level of precedence\, since there isn't currently one.

if that's always done\, so consideration should then be given to having a variant of "~" that explicitly makes the matcher expression dynamic.

Or the other way round.

Think of the Huffman coding​: a constant type is the usual case. However\, if matcher-generating functions routinely have call checkers for constant folding\, maybe we could arrange for most matcher-constructing expressions to constant-fold. That could remove the pressure for implicit init-once semantics\, in which case we can just go with always-dynamic semantics. In that case\, the occasional non-constant one that we want to make constant can be handled by explicit use of state variable\, Memoize​::Lift\, et al.

_~ -- usable\, but requires whitespace​: previous predicate is `under'stood

Not really usable\, because it would interact with nameless parameters. "($_~...)" looks like an attempt to name the parameter "$_". We don't permit $_ as a lexical variable any more\, but because of the syntactic proximity and the history we shouldn't make "$_" mean something else in signatures.

In optional parameters I imagine the default value expression would be exempt from the type constraint\, but this could go either way.

Skipping the constraint would be faster. That would assume the programmer knows what he is doing\, which is not an unreasonable assumption.

I was thinking more about deliberate use of an out-of-constraint default. In some cases that could be used as an unambiguous indicator that the argument wasn't passed in. I have difficulty imagining a case where an out-of-constraint default could actually be a useful value for the parameter\, requiring no explicit check later\, but it probably could happen. I think those are reasonable things to do\, which we shouldn't lightly prohibit. On the other hand\, mistaken out-of-constraint defaults probably wouldn't happen much​: most default value expressions are simple\, and it's easy for the programmer to make sure they're of the right type.

In terms of what the smartmatcher sees\, I think the only reasonable behaviour is to do the aliasing first\, and then apply the type constraint to the lexical variable created by the parameter.

Is it? With \$x type aliasing\, it would make just as much sense to apply the type constraint to \$x\, or equivalently to the actual argument.

With \@​x aliasing\, we can't apply the smartmatch to @​x itself\, only to \@​x. This argues in favour of smartmatching on \$x for \$x.

So (\$x~Str) will effectively do (\$_[0] ~~ Str)

That doesn't make sense. Perhaps you meant "(${$_[0]} ~~ Str)".

In the former case\, $_[0] would not necessarily be a ref\, but in the latter case it would have to be.

Those are the other way round\, according to the aliasing rules that I proposed.

There is still the question of what order the \ and ~ come​:

Yeah. "($x~Str\)" seems more logical to me\, but it does raise a tokenisation issue. It's less of a problem if we actually implement the "\=" operator as Ilmari suggested\, but that still leaves us with bare "\" having to be treated as an infix operator of assignment precedence which it normally isn't. The aliasing syntax seems more malleable here\, so might be the better side from which to approach it. I don't have a firm opinion about this\, other than that I wouldn't accept a fudge. This is all new syntax\, we can get it right.

-zefram

p5pRT commented 6 years ago

From @cpansprout

On Mon\, 20 Nov 2017 10​:03​:05 -0800\, sprout wrote​:

I will trying brainstorming with every symbol of my keyboard​:

Sigh. If I don’t proofreading my own writing\, I end up sounding like Google Translate.

--

Father Chrysostomos

p5pRT commented 6 years ago

From @cpansprout

On Mon\, 20 Nov 2017 10​:46​:50 -0800\, zefram@​fysh.org wrote​:

Father Chrysostomos via RT wrote​:

?​: is also immensely useful. Make ?​: the lowest precedence permitted.

That's the precedence level immediately above assignment\, so that just fits. We should add a parse_*expr() function for that level of precedence\, since there isn't currently one.

Perhaps parse_condexpr().

if that's always done\, so consideration should then be given to having a variant of "~" that explicitly makes the matcher expression dynamic.

Or the other way round.

Think of the Huffman coding​: a constant type is the usual case. However\, if matcher-generating functions routinely have call checkers for constant folding\, maybe we could arrange for most matcher-constructing expressions to constant-fold. That could remove the pressure for implicit init-once semantics\, in which case we can just go with always-dynamic semantics. In that case\, the occasional non-constant one that we want to make constant can be handled by explicit use of state variable\, Memoize​::Lift\, et al.

I think I prefer this idea.

In terms of what the smartmatcher sees\, I think the only reasonable behaviour is to do the aliasing first\, and then apply the type constraint to the lexical variable created by the parameter.

Is it? With \$x type aliasing\, it would make just as much sense to apply the type constraint to \$x\, or equivalently to the actual argument.

With \@​x aliasing\, we can't apply the smartmatch to @​x itself\, only to \@​x. This argues in favour of smartmatching on \$x for \$x.

But you already proposed earlier than (@​x~foo) would implicitly enreference \@​x whereas ($x~foo) will not.

If we want *some* consistency\, then array parameters should always get one level of referencing more than scalar parameters.

So (\$x~Str) will effectively do (\$_[0] ~~ Str)

That doesn't make sense. Perhaps you meant "(${$_[0]} ~~ Str)".

In the former case\, $_[0] would not necessarily be a ref\, but in the latter case it would have to be.

Those are the other way round\, according to the aliasing rules that I proposed.

Yeah\, I wrote it really quickly.

There is still the question of what order the \ and ~ come​:

Yeah. "($x~Str\)" seems more logical to me\, but it does raise a tokenisation issue. It's less of a problem if we actually implement the "\=" operator as Ilmari suggested\, but that still leaves us with bare "\" having to be treated as an infix operator of assignment precedence which it normally isn't. The aliasing syntax seems more malleable here\, so might be the better side from which to approach it. I don't have a firm opinion about this\, other than that I wouldn't accept a fudge. This is all new syntax\, we can get it right.

There is a fundamental problem with the way the parsing functions work. Namely\, the token that follows the code being parsed must be recognizable by the lexer as being of a particular precedence. This severely limits the usefulness of recursive-descent parsing functions.

In an ideal world\, parse_fooexpr() would parse as much as possible until it reaches a token that does fit in the ‘foo’ rule in the grammar. (Sorry\, I can’t remember offhand the term for that.)

I am by no means an experienced yacc user\, so I don’t know how you go about this. But I think it would entail changing the way errors are handled in perly.y. Currently sideff is defined in terms of error | ....

With strict recursive-descent parsing (not involving any yaks)\, for each precedence level you have a function that does​:

  if (!parse_lower_precedence())   position = old_position;

and it’s pretty straightforward. I don’t know whether perl could switch to something like that\, though\, because of do{BEGIN...} etc.

Fixing this\, however\, would open up the parsing possibilities without adding more special cases to the lexer for the tokens we need to end on.

--

Father Chrysostomos

p5pRT commented 6 years ago

From @Leont

On Mon\, Nov 20\, 2017 at 3​:59 PM\, Zefram \perlbug\-followup@&#8203;perl\.org wrote​:

Everyone would like to have type constraints in signatures\, but a prohibitive issue is that the core doesn't have a general type system. (There are classes\, but they don't address any of the type distinctions we frequently need to make between unblessed data types.) Smartmatch could become the core's type system\, as we've discussed a bit when talking about the future of smartmatch.

Suppose that the smartmatch/switch discussions lead to a version of smartmatch that we can all live with. (In the last round of discussion we seemed to be agreed on smartmatch semantics\, and it's just the given/when stuff that's holding us back.) This would be almost entirely based on explicit overloading of the smartmatch operator by blessed objects that appear on the rhs of smartmatch. We would expect to see a rich ecosystem of matcher object classes that implement all kinds of predicate\, including composite predicates that incorporate subordinate matcher objects.

Matcher objects can then be used as type metaobjects. In general they're types in only the minimal sense that they have a concept of type membership. Specific kinds of matcher object may also have other kinds of type-related behaviour; for example\, Moose class metaobjects would presumably overload smartmatch to implement a class membership check\, while also having introspective methods that only make sense for the Moose concept of a class. But we don't need to worry about that; for signatures the only aspect of a type that we care about is the membership predicate.

So a scalar signature item could specify a type constraint by referring to an appropriate matcher object. We'd want to permit an arbitrary expression for the matcher object\, to allow for parameterised and composite types. So very little signature syntax is required; really just a signifier for smartmatching and a grammatical slot into which to put the matcher expression. Using signature syntax otherwise as it is today\, this would look something like​:

use Smart\_Type qw\(Num Str union HashRef\_of\);   \# imaginary module
use feature "signatures";
sub gronk \($why ~Str\, $how\_hard ~Num = 5\) \{\.\.\.\}
sub annotate \($subject\, $msg ~ union\(Str\, HashRef\_of\(Str\)\)\) \{\.\.\.\}
sub nom \($food ~ My&#8203;::Food\->meta = My&#8203;::Apple\->new\) \{\.\.\.\}

"~" references the smartmatch operator while not being longer than necessary. The matcher expression would have to be restricted to expressions above some middling precedence level\, so that in the case of an optional parameter it doesn't read the "=" and default value expression as part of the matcher expression. But I imagine matcher objects would also overload the bitwise operators to perform Boolean compositions\, so it would be nice for the threshold to be lower than those operators. Any expression of lower precedence than the threshold could of course be parenthesised. So we'd have things like​:

use Smart\_Type qw\(Str HashRef\_of\);
use feature "signatures";
sub annotate \($subject\, $msg ~ Str | HashRef\_of\(Str\)\) \{\.\.\.\}
sub frob \($spec ~ \(state $fspec\_type = gen\_fspec\_type\(\)\)\) \{\.\.\.\}

With respect to the last of those examples\, usually the type we want to smartmatch against is fixed for all time\, and recomputing the matcher for each execution of the sub would be unwanted expense\, potentially significant. So making the matcher be implicitly saved in an invisible state variable might be a better way to go. However\, it's impossible to get fully dynamic type constraint logic (referring to earlier parameters) if that's always done\, so consideration should then be given to having a variant of "~" that explicitly makes the matcher expression dynamic.

In optional parameters I imagine the default value expression would be exempt from the type constraint\, but this could go either way.

It's a little more difficult to apply type constraints to array and hash parameters. We probably want to apply the type constraint to the whole array or hash\, for maximum flexibility\, but the actual smartmatch operand would have to be *a reference to* the array or hash\, so how do we indicate that in the signature syntax?

It's also necessary to think about how type constraints would interact with aliasing in signatures [perl #132472].

I don't really see the point of discussing this now\, when we're probably years away from having stable and sane smartmatch.

Leon