Skip Menu |

This queue is for tickets about the Type-Tiny CPAN distribution.

Report information
The Basics
Id: 131666
Status: resolved
Priority: 0/
Queue: Type-Tiny

People
Owner: Nobody in particular
Requestors: bpjonsson [...] gmail.com
Cc:
AdminCc:

Bug Information
Severity: (no value)
Broken in: (no value)
Fixed in: (no value)



Subject: Question: recursive container types
Date: Tue, 4 Feb 2020 14:54:04 +0100
To: bug-Type-Tiny [...] rt.cpan.org
From: BP Jonsson <bpjonsson [...] gmail.com>
This is a question rather than a bug, but I guess it could also be construed as a feature request. Apologies if this could/should go over IRC, but IRC is difficult because of my cerebral palsy; I keep hitting the wrong keys at the wrong time... I have a bit of a problem. I want to check for a hashref which may contain scalars, arrayrefs or further hashrefs basically to any depth, but where non-hashref, non-arrayref values must be strings or objects with overloaded stringification, so I guess that I would need to define type constraints for such arrays and hashes which nest themselves, presumably as weak references. Is that at all possible with Type::Tiny? I suppose not, so is there anything I can do short of traversing the whole data structure "manually" at every turn? As I said this should probably *not* be taken as a feature request although I suppose that in principle you could check for recursive inclusions in nested types like HashRef and make references weak as needed, but no need to actually implement that at this time; I'm rather looking for good alternative strategies.
That's a very good question. Basically what you're asking for is something like: MyHashRef := HashRef[ Str | MyHashRef ] (And yes, I know you also mentioned ArrayRefs and overloaded objects, but cutting it down to a simple example here.) This can't be done directly because MyHashRef isn't defined yet when the list of parameters is being passed to HashRef. However, you can probably work around it using something like this: MyHashRef := HashRef[ Str | HashRef->where( 'MyApp::MyTypes::is_MyHashRef($_)' ) ]; I haven't actually tested this though. It's something I should probably document and test. I believe this DOES work in MooseX::Types. One of the few advantages of having a central type registry.
To be clear, the is_MyHashRef() function would be the one automatically created when you define the type MyHashRef in a type library, not a function you're expected to write for yourself.
Attached is a small script showing proof-of-concept.
Subject: test.pl
use strict; use warnings; use Test::More; BEGIN { package MyTypes; use Type::Library -base; use Types::Standard -types; __PACKAGE__->add_type( name => 'MyHashRef', parent => HashRef[ Int | HashRef->where('MyTypes::is_MyHashRef($_)') ], ); $INC{'MyTypes.pm'} = __FILE__; # stop `use` from complaining }; use MyTypes -types; my %good1 = ( foo => 1, bar => 2 ); my %good2 = ( %good1, bat => {}, baz => { foo => 3 } ); my %good3 = ( %good2, quux => { quuux => { quuuux => 0, xyzzy => {} } } ); my %bad1 = ( %good1, bar => \1 ); my %bad2 = ( %good2, baz => { foo => \1 } ); my %bad3 = ( %good3, quux => { quuux => { quuuux => 0, xyzzy => \1 } } ); ok( MyHashRef->can_be_inlined ); ok( MyHashRef->check( {} ) ); ok( MyHashRef->check( \%good1 ) ); ok( MyHashRef->check( \%good2 ) ); ok( MyHashRef->check( \%good3 ) ); ok( ! MyHashRef->check( \%bad1 ) ); ok( ! MyHashRef->check( \%bad2 ) ); ok( ! MyHashRef->check( \%bad3 ) ); ok( ! MyHashRef->check( undef ) ); ok( ! MyHashRef->check( \1 ) ); done_testing;
I know you said this wasn't a feature request, but it was actually pretty simple to add it as a feature: https://github.com/tobyink/p5-type-tiny/compare/feature/reentrant-types It will only work in type libraries; not if you're just defining types in lexical variables. This is because when the type recursively calls itself, it needs to be able to find itself, so it needs to look up the type by name from a package. (If that makes sense?) You need to declare the type name ahead of the definition: use Type::Library -base, -declare => qw( MyHashRef ); And then you can just define it in the type library like this: __PACKAGE__->add_type( name => MyHashRef, parent => HashRef[ Int | MyHashRef ], ); If your needs are significantly more complex, then you're out of luck. (No recursively defined coercions, for example.) I hope to include this in Type::Tiny 1.010 when it's released.
Subject: Re: [rt.cpan.org #131666] Question: recursive container types
Date: Wed, 5 Feb 2020 14:17:02 +0100
To: bug-Type-Tiny [...] rt.cpan.org
From: BP Jonsson <bpjonsson [...] gmail.com>
Wow that's wonderful! Prompt response indeed! Thanks a lot! Will it be possible to do something like the (probably incomplete) example below? If so I'll be all set. ``````perl use Type::Library -declare => ( StringArray, StringHash ); __PACKAGE__->add_type( name => StringArray, parent => ArrayRef->of( StringLike | StringArray | StringHash ); __PACKAGE__->add_type( name => StringHash, parent => HashRef->of( StringLike | StringHash | StringArray ); `````` Den tis 4 feb. 2020 17:33Toby Inkster via RT <bug-Type-Tiny@rt.cpan.org> skrev: Show quoted text
> <URL: https://rt.cpan.org/Ticket/Display.html?id=131666 > > > I know you said this wasn't a feature request, but it was actually pretty > simple to add it as a feature: > > https://github.com/tobyink/p5-type-tiny/compare/feature/reentrant-types > > It will only work in type libraries; not if you're just defining types in > lexical variables. This is because when the type recursively calls itself, > it needs to be able to find itself, so it needs to look up the type by name > from a package. (If that makes sense?) > > > You need to declare the type name ahead of the definition: > > use Type::Library -base, -declare => qw( MyHashRef ); > > And then you can just define it in the type library like this: > > __PACKAGE__->add_type( > name => MyHashRef, > parent => HashRef[ Int | MyHashRef ], > ); > > If your needs are significantly more complex, then you're out of luck. (No > recursively defined coercions, for example.) > > I hope to include this in Type::Tiny 1.010 when it's released. >
On 2020-02-05T13:17:20Z, bpjonsson@gmail.com wrote: Show quoted text
> Will it be possible to do something like the (probably incomplete) > example below? If so I'll be all set.
I believe that should work, though I haven't tested it.
I added this as a test case: https://github.com/tobyink/p5-type-tiny/commit/81cfbe0ea1f0fd68fc056474f1d3063792629e1e (I used Str rather than StrLike, but there's nothing special about StrLike that would stop it also working.)
Subject: Re: [rt.cpan.org #131666] Question: recursive container types
Date: Wed, 5 Feb 2020 19:14:03 +0100
To: bug-Type-Tiny [...] rt.cpan.org
From: BP Jonsson <bpjonsson [...] gmail.com>
Great! Thanks again! I really appreciate the work you do, not just this. Den ons 5 feb. 2020 17:19Toby Inkster via RT <bug-Type-Tiny@rt.cpan.org> skrev: Show quoted text
> <URL: https://rt.cpan.org/Ticket/Display.html?id=131666 > > > I added this as a test case: > > > https://github.com/tobyink/p5-type-tiny/commit/81cfbe0ea1f0fd68fc056474f1d3063792629e1e > > (I used Str rather than StrLike, but there's nothing special about StrLike > that would stop it also working.) >
Stable release 1.010000 includes this.