Re: [perl #121454] Can't inline complex constraints in multisub signatures
On 2014-03-29 21:45, Damian Conway wrote: Moritz wrote: To spin the tale further, we need to think about what happens if somebody writes multi foo(1|2e0) { ... } so now we have Int|Num. We could explore the most-derived common ancestor (Cool), or look into role space (Real, Numeric come to mind), or simply error out. Or maybe we need to reconsider the whole idea that it's appropriate to infer type from a smartmatched constraint? [...] In other words specifying a constraint value is a way of applying a smartmatched acceptance test to a parameter, but the type of the acceptance test is typically totally unrelated to the type of the parameter. Which is why it now seems very odd to me that we are currently inferring parameter types from constraint values. I couldn't agree more. This looks like a piece of odd-sized baggage left behind by Moose, where declaring type constraints on attributes is too easily mistaken as actual type declarations. Other languages may provide simple restriction rules for parameter value domains in the shape of types, but in a dynamically typed language, it doesn't make much sense, since there may be conversion paths between the types involved. I'd much more prefer a pure matching language where no assumptions about types are done automatically, forcing me to write explicit type constraints myself, if I believe they are necessary (they should rarely be needed, since having to deal too much with types in a dynamically typed language is counterproductive :-) ). In general - Perl (5) is a great tool because it doesn't force people to deal too much with typing (both implicit and explicit). Lets keep it that way. -- Michael Zedeler 70 25 19 99 mich...@zedeler.dk mailto:mich...@zedeler.dk dk.linkedin.com/in/mzedeler http://dk.linkedin.com/in/mzedeler/ | twitter.com/mzedeler https://twitter.com/mzedeler | github.com/mzedeler https://github.com/mzedeler/
Re: [perl #121454] Can't inline complex constraints in multisub signatures
On 2014-03-31 14:49, Michael Zedeler. wrote: On 2014-03-29 21:45, Damian Conway wrote: Moritz wrote: To spin the tale further, we need to think about what happens if somebody writes multi foo(1|2e0) { ... } so now we have Int|Num. We could explore the most-derived common ancestor (Cool), or look into role space (Real, Numeric come to mind), or simply error out. Or maybe we need to reconsider the whole idea that it's appropriate to infer type from a smartmatched constraint? [...] In other words specifying a constraint value is a way of applying a smartmatched acceptance test to a parameter, but the type of the acceptance test is typically totally unrelated to the type of the parameter. Which is why it now seems very odd to me that we are currently inferring parameter types from constraint values. I couldn't agree more. This looks like a piece of odd-sized baggage left behind by Moose, where declaring type constraints on attributes is too easily mistaken as actual type declarations. Sorry - correction: where declaring type constraints should be where declaring value constraints. -- Michael Zedeler 70 25 19 99 mich...@zedeler.dk mailto:mich...@zedeler.dk dk.linkedin.com/in/mzedeler http://dk.linkedin.com/in/mzedeler/ | twitter.com/mzedeler https://twitter.com/mzedeler | github.com/mzedeler https://github.com/mzedeler/
Re: [perl #121454] Can't inline complex constraints in multisub signatures
Moritz wrote: To spin the tale further, we need to think about what happens if somebody writes multi foo(1|2e0) { ... } so now we have Int|Num. We could explore the most-derived common ancestor (Cool), or look into role space (Real, Numeric come to mind), or simply error out. Or maybe we need to reconsider the whole idea that it's appropriate to infer type from a smartmatched constraint? Because, having pondered it at some length, I think we could very reasonably decide that, when providing a constraint, the coder is not making any reliable implication regarding the associated parameter type. Let's look at some examples... 1. Suppose we wanted a subroutine that classifies numbers according to a very simple scheme. We might write: multi classify($ where 0 ) { 'zero' } multi classify($ where 1..9) { 'digit'} multi classify($ ) { 'sequence' } It would be more convenient if we could just write: multi classify( 0) { 'zero' } multi classify( 1..9 ) { 'digit'} multi classify( $) { 'sequence' } But we can't, because the current inference rules would infer: multi classify(Int $ where 0 ) { 'zero' } multi classify(List[Int] $ where 1..9) { 'digit'} multi classify(Any $ where * ) { 'sequence' } which is not helpful. It goes wrong because, except in the first version of Cclassify, the type of the parameter constraint does not imply the type of parameter. 2. Or suppose we wanted a subroutine that only ever accepts a given argument once. We might write: sub unseen ($msg) { state %seen; %seen{$msg}++; } multi ping($ where unseen ) { die 'One ping only!' } multi ping($msg) { say PING! ($msg) } It would be more convenient if we could write: multi ping({.unseen}) { die 'One ping only!' } multi ping($msg ) { say PING! ($msg) } But we can't, because the current inference rules would make that: multi ping(Block $ where {.unseen}) { die 'One ping only!' } which is not helpful. Because, again, the type of the constraint does not imply (nor is it even directly related to) the type of the parameter. 3. Or suppose we wanted to detect special boundary conditions (a case similar to Moritz's example above). We might write: multi check_value($ where 0|1e6) { fail 'edge case!' } multi check_value($) { return True } It would be more convenient if we could write: multi check_value(0|1e6) { fail 'edge case!' } multi check_value($) { return True } But we can't, because the current inference rules would make that: multi check_value(Junction $ where 0|1e6) { fail 'edge case!' } multi check_value( $) { return True } which is not helpful. Because, as before, the type of the constraint is not correlated with the type of the parameter. In each case (and in the majority of other cases, I suspect) the type and the constraint value are performing two entirely distinct tasks, and are often unrelated. Or, at least, unrelated in the sense that the constraint value is frequently not type-compatible with the type, because the constraint is verified by smartmatching, which is most often an operation between values of two unrelated types: integer matched against block, string matched against list, number matched against junction, etc. etc. In other words specifying a constraint value is a way of applying a smartmatched acceptance test to a parameter, but the type of the acceptance test is typically totally unrelated to the type of the parameter. Which is why it now seems very odd to me that we are currently inferring parameter types from constraint values. The type of the constraint value tells us nothing reliable about the type of the parameter it constrains. It only tells us that the value of that parameter must be matchable against the value of that constraint. And that appears to be true even for the very simplest example: sub foo($ where 1) { say 'There can be only one!' } foo(1); # Okay foo('1');# Okay foo(True); # Okay foo([42]); # Okay # et cetera... I ought to be able to write that as: sub foo(1) { say 'There can be only one!' } but I cannot, because the current rules don't even infer a coercive type. TL;DR: In general, the type of a parameter's Cwhere constraint value has little to do with the type of the parameter, because constraints are smartmatched, not type-matched. My proposal, therefore, is that any parameter specified only as a constraint value simply does not attempt to infer its parameter type at all, but just retains its default type of Any. That is, in all cases: sub foo( SOME_VALUE ) {...} is just a shorthand for: sub foo($ where SOME_VALUE )
Re: [perl #121454] Can't inline complex constraints in multisub signatures
On 03/28/2014 02:28 PM, Parrot Raiser wrote: On 3/27/14, Moritz Lenz mor...@faui2k3.org wrote: Agreed. We just need to come up with a consistent, intuitive way to handle the rest of the cases. And implement it. Whenever somebody offers a solution to a problem formulated as We just need to (or why don't you just?), it's usually a sign that they've overlooked some fundamental aspect of the problem. Or, as I suspect in this case, poured irony all over it. :-)* Sorry, it's not meant ironic at all. I just meant to indicate that speccing without implementation is a useless exercise, and right now I'm not seeing myself as the one who thinks through and decides on all the corner cases, and implements it. Cheers, Moritz
Re: [perl #121454] Can't inline complex constraints in multisub signatures
On 03/19/2014 11:06 AM, Damian Conway wrote: To me, the issue is: how does Perl 6 actually carry out the type inference we're doing here? And I believe it's an important question to get right, as we may eventually see Perl 6 doing more type inference...either in core, or else through some nefarious module that some evil genius eventually writes. ;-) If Perl 6 does type inference on some $value simply by calling $value.WHAT, then: multi foo(0|1) {...} should indeed be equivalent to: multi foo(Junction $ where 0|1) {...} However, that's not the only possibility (nor, in my opinion, the most predictable or useful). If, for example, Perl 6 did type inference by calling an internal: multi infer-type (Any $value) { $value.WHAT } then: multi foo(0|1) {...} would be equivalent to: multi foo(Int|Int $ where 0|1) {...} which is, of course, just: multi foo(Int $ where 0|1) {...} No. The type of Int|Int is still Junction, not Int. However you turn the problem, you'll need some form of special-casing to fold Int|Int into Int, and please formulate it so that the Mu case continues to work. To spin the tale further, we need to think about what happens if somebody writes multi foo(1|2e0) { ... } so now we have Int|Num. We could explore the most-derived common ancestor (Cool), or look into role space (Real, Numeric come to mind), or simply error out. which, in my view, would be a much more useful outcome in the vast majority of cases. Agreed. We just need to come up with a consistent, intuitive way to handle the rest of the cases. And implement it. Cheers, Moritz I accept that it's important to be able to explicitly declare a parameter as a Junction, so it's immune to junctive autothreading, but I don't think that should be the norm...and certainly shouldn't happen implicitly. That's why variables, parameters, and return values that are not explicitly typed default to Any, rather than to Junction. And, I think this is another implicit case where a non-junctive outcome would be more useful...and more widely expected. In other words, for the one time in a thousand (or fewer) where I actually want: sub foo(Junction $ where 0|1) {...} then I should have to be explicit about that argument being junctive (because I should *always* have to be explicit about an argument being junctive). And for the 999 times I want: sub foo(Any $ where 0|1) {...} I should be able to just write: sub foo(0|1) {...} Damian
Re: [perl #121454] Can't inline complex constraints in multisub signatures
f'up to p6l, because the ticket doesn't need the rest of the discussion. On 03/19/2014 10:21 PM, Darren Duncan wrote: On 2014-03-19, 1:20 AM, Moritz Lenz wrote: On 03/19/2014 12:45 AM, Darren Duncan wrote: Damian, Moritz, etc, It seems to me that the basic problem here is that the vertical bar | has 2 different meanings (outside rules) depending on context. When used with type names it produces a union type, while with normal values such as integer literals it produces a junction value. No. It's always a Junction. Perl 6 has no union types. Really? All this time I thought in Perl 6 you could write say: my Int|Str $foo; You were wrong. The only way to achieve something comparble is subset IntStr of Any where Int|Str but for example for multi dispatch, it matters that not a nominal type matched, but rather a constraint, so that's a not a great replacement. Or otherwise say Int|Str anywhere a type name could go, and then this is saying you accept anything that is a Int or a Str but nothing else there. Was that never true or was it replaced by something while I wasn't looking? There was something like that in the specs, but no concept whatsoever of how that might work. You can't just slap on union types onto an existing type system, and have it magically work out. In languages like Haskell where they work fine, they are right at the core of the language. So the feature was removed years ago, because it's not really implementable. Cheers, Moritz