class IO::Path::Parts

IO::Path parts encapsulation

class IO::Path::Parts does Positional does Associative does Iterable { }

An IO::Path::Parts object is a container for the parts of an IO::Path object. It is usually created with a call to the method .parts on a IO::Path object. It can also be created with a call to the method .split on a object of one of the low-level path operations sub-classes of IO::Spec.

The parts of an IO::Path are:

Methods

method new

method new(\volume, \dirname, \basename)

Create a new IO::Path::Parts object with \volume, \dirname and \basename as respectively the volume, directory name and basename parts.

attribute volume

Read-only. Returns the volume of the IO::Path::Parts object.

IO::Path::Parts.new('C:''/some/dir''foo.txt').volume.say;
# OUTPUT: «C:␤» 

attribute dirname

Read-only. Returns the directory name part of the IO::Path::Parts object.

IO::Path::Parts.new('C:''/some/dir''foo.txt').dirname.say;
# OUTPUT: «/some/dir␤» 

attribute basename

Read-only. Returns the basename part of the IO::Path::Parts object.

IO::Path::Parts.new('C:''/some/dir''foo.txt').basename.say;
# OUTPUT: «foo.txt␤» 

Previous implementations

Before Rakudo 2020.06 the .parts method of IO::Path returned a Map and the .split routine of the IO::Spec sub-classes returned a List of Pair. The IO::Path::Part class maintains compatibility with these previous implementations by doing Positional, Associative and Iterable.

my $parts = IO::Path::Parts.new('C:''/some/dir''foo.txt');
say $parts<volume>;      # OUTPUT: «C:␤» 
say $parts[0];           # OUTPUT: «volume => C:␤» 
say $parts[0].^name;     # OUTPUT: «Pair␤» 
.say for $parts[];
# OUTPUT: «volume => C:␤dirname => /some/dir␤basename => foo.txt␤» 

Type Graph

Type relations for IO::Path::Parts
raku-type-graph IO::Path::Parts IO::Path::Parts Any Any IO::Path::Parts->Any Positional Positional IO::Path::Parts->Positional Associative Associative IO::Path::Parts->Associative Iterable Iterable IO::Path::Parts->Iterable Mu Mu Any->Mu

Expand above chart

Routines supplied by role Positional

IO::Path::Parts does role Positional, which provides the following routines:

(Positional) method of

method of()

Returns the type constraint for elements of the positional container, that is, the T in the definition above, which, as it can be seen, defaults to Mu. It is returned as a type object.

my @þ;
say @þ.of.^name;   # OUTPUT: «Mu␤ 
my Str @þð;
say @þð.of.raku;   # OUTPUT: «Str␤» 
say (my int @).of# OUTPUT: «(int)␤» 

(Positional) method elems

method elems()

Should return the number of available elements in the instantiated object.

(Positional) method AT-POS

method AT-POS(\position)

Should return the value / container at the given position.

(Positional) method EXISTS-POS

method EXISTS-POS(\position)

Should return a Bool indicating whether the given position actually has a value.

(Positional) method STORE

method STORE(\values:$INITIALIZE)

This method should only be supplied if you want to support the:

my @a is Foo = 1,2,3;

syntax for binding your implementation of the Positional role.

Should accept the values to (re-)initialize the object with. The optional named parameter will contain a True value when the method is called on the object for the first time. Should return the invocant.

Routines supplied by role Associative

IO::Path::Parts does role Associative, which provides the following routines:

(Associative) method of

Defined as:

method of()

Associative, as the definition above shows, is actually a parameterized role which can use different classes for keys and values. As seen at the top of the document, by default it coerces the key to Str and uses a very generic Mu for value.

my %any-hash;
say %any-hash.of# OUTPUT: «(Mu)␤»

The value is the first parameter you use when instantiating Associative with particular classes:

class DateHash is Hash does Associative[Cool,DateTime{};
my %date-hash := DateHash.new;
say %date-hash.of# OUTPUT: «(Cool)␤»

(Associative) method keyof

Defined as:

method keyof()

Returns the parameterized key used for the Associative role, which is Any coerced to Str by default. This is the class used as second parameter when you use the parameterized version of Associative.

my %any-hash;
%any-hash.keyof# OUTPUT: «(Str(Any))␤»

(Associative) method AT-KEY

method AT-KEY(\key)

Should return the value / container at the given key.

class What { method AT-KEY(\key{ 42 }};
say What.new{33}# OUTPUT: «42␤» 

(Associative) method EXISTS-KEY

method EXISTS-KEY(\key)

Should return a Bool indicating whether the given key actually has a value.

(Associative) method STORE

method STORE(\values:$INITIALIZE)

This method should only be supplied if you want to support the:

my %h is Foo = => 42=> 666;

syntax for binding your implementation of the Associative role.

Should accept the values to (re-)initialize the object with, which either could consist of Pairs, or separate key/value pairs. The optional named parameter will contain a True value when the method is called on the object for the first time. Should return the invocant.

Routines supplied by role Iterable

IO::Path::Parts does role Iterable, which provides the following routines:

(Iterable) method iterator

Defined as:

method iterator(--> Iterator:D)

Method stub that ensures all classes doing the Iterable role have a method iterator.

It is supposed to return an Iterator.

say (1..10).iterator;

(Iterable) method flat

Defined as:

method flat(Iterable:D: --> Iterable)

Returns another Iterable that flattens out all iterables that the first one returns.

For example

say (<a b>'c').elems;         # OUTPUT: «2␤» 
say (<a b>'c').flat.elems;    # OUTPUT: «3␤»

because <a b> is a List and thus iterable, so (<a b>, 'c').flat returns ('a', 'b', 'c'), which has three elems.

Note that the flattening is recursive, so ((("a", "b"), "c"), "d").flat returns ("a", "b", "c", "d"), but it does not flatten itemized sublists:

say ($('a''b'), 'c').raku;    # OUTPUT: «($("a", "b"), "c")␤»

You can use the hyper method call to call the .List method on all the inner itemized sublists and so de-containerize them, so that flat can flatten them:

say ($('a''b'), 'c')>>.List.flat.elems;    # OUTPUT: «3␤»

(Iterable) method lazy

Defined as:

method lazy(--> Iterable)

Returns a lazy iterable wrapping the invocant.

say (1 ... 1000).is-lazy;      # OUTPUT: «False␤» 
say (1 ... 1000).lazy.is-lazy# OUTPUT: «True␤»

(Iterable) method hyper

Defined as:

method hyper(Int(Cool:$batch = 64Int(Cool:$degree = 4)

Returns another Iterable that is potentially iterated in parallel, with a given batch size and degree of parallelism.

The order of elements is preserved.

say ([1..100].hyper.map({ $_ +1 }).list);

Use hyper in situations where it is OK to do the processing of items in parallel, and the output order should be kept relative to the input order. See race for situations where items are processed in parallel and the output order does not matter.

Options degree and batch

The degree option (short for "degree of parallelism") configures how many parallel workers should be started. To start 4 workers (e.g. to use at most 4 cores), pass :4degree to the hyper or race method. Note that in some cases, choosing a degree higher than the available CPU cores can make sense, for example I/O bound work or latency-heavy tasks like web crawling. For CPU-bound work, however, it makes no sense to pick a number higher than the CPU core count.

The batch size option configures the number of items sent to a given parallel worker at once. It allows for making a throughput/latency trade-off. If, for example, an operation is long-running per item, and you need the first results as soon as possible, set it to 1. That means every parallel worker gets 1 item to process at a time, and reports the result as soon as possible. In consequence, the overhead for inter-thread communication is maximized. In the other extreme, if you have 1000 items to process and 10 workers, and you give every worker a batch of 100 items, you will incur minimal overhead for dispatching the items, but you will only get the first results when 100 items are processed by the fastest worker (or, for hyper, when the worker getting the first batch returns.) Also, if not all items take the same amount of time to process, you might run into the situation where some workers are already done and sit around without being able to help with the remaining work. In situations where not all items take the same time to process, and you don't want too much inter-thread communication overhead, picking a number somewhere in the middle makes sense. Your aim might be to keep all workers about evenly busy to make best use of the resources available.

You can also check out this blog post on the semantics of hyper and race

(Iterable) method race

Defined as:

method race(Int(Cool:$batch = 64Int(Cool:$degree = 4 --> Iterable)

Returns another Iterable that is potentially iterated in parallel, with a given batch size and degree of parallelism (number of parallel workers).

Unlike hyper, race does not preserve the order of elements (mnemonic: in a race, you never know who will arrive first).

say ([1..100].race.map({ $_ +1 }).list);

Use race in situations where it is OK to do the processing of items in parallel, and the output order does not matter. See hyper for situations where you want items processed in parallel and the output order should be kept relative to the input order.

Blog post on the semantics of hyper and race

See hyper for an explanation of :$batch and :$degree.