Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Array of objects

Tags:

raku

Let's say I want to connect to two package repositories, make a query for a package name, combine the result from the repos and process it (filter, unique, prioritize,...), What is a good way to do that?

What I though about is creating Array of two Cro::HTTP::Client objects (with base-uri specific to each repo), and when I need to make HTTP request I call @a>>.get, then process the result from the repos together.

I have attached a snippet of what I'm trying to do. But I would like to see if there is a better way to do that. or if the approach mention in the following link is suitable for this use case! https://perl6advent.wordpress.com/2013/12/08/day-08-array-based-objects/

use Cro::HTTP::Client;

class Repo {

  has $.name;
  has Cro::HTTP::Client $!client;
  has Cro::Uri $.uri;
  has Bool $.disable = False;

  submethod TWEAK () {
    $!client = Cro::HTTP::Client.new(base-uri => $!uri, :json);
  }

  method get (:$package) {

    my $path = <x86_64?>;
    my $resp = await $!client.get($path ~ $package);
    my $json = await $resp.body;
    return $json;
  }
}


class AllRepos {

  has Repo @.repo;

  method get (:$package) {

    # check if some repos are disabled
    my @candidate = @!repo>>.get(:$package).unique(:with(&[eqv])).flat;

    # do furthre processign of the data then return it;
    return @candidate;

  }
}

my $repo1 = Repo.new: name => 'repo1', uri => Cro::Uri.new(:uri<http://localhost:80>);
my $repo2 = Repo.new: name => 'repo2', uri => Cro::Uri.new(:uri<http://localhost:77>);

my @repo = $repo1, $repo2;

my $repos = AllRepos.new: :@repo;


#my @packages = $repos.get: package => 'rakudo';

like image 211
hythm Avatar asked May 22 '19 21:05

hythm


1 Answers

Let's say I want to connect to two package repositories, make a query for a package name, combine the result from the repos and process it (filter, unique, prioritize,...), What is a good way to do that?

The code you showed looks like one good way in principle but not, currently, in practice.

The hyperoperators such as >>:

  • Distribute an operation (in your case, connect and make a query) ...

  • ... to the leaves of one or two input composite data structures (in your case the elements of one array @!repo) ...

  • ... with logically parallel semantics (by using a hyperoperator you are declaring that you are taking responsibility for thinking that the parallel invocations of the operation will not interfere with each other, which sounds reasonable for connecting and querying) ...

  • ... and then return a resulting composite data structure with the same shape as the original structure if the hyperoperator is a unary operator (which applies in your case, because you applied >>, which is an unary operator which takes a single argument on its left, so the result of the >>.get is just a new array, just like the input @!repo) or whose shape is the hyper'd combination of the shapes of the pair of structures if the hyperoperator is a binary operator, such as >>op<< ...

  • ... which can then be further processed (in your case it is, with .unique, which will produce a resulting Seq) ...

  • ... whose elements you then assign back into another array (@candidate).

So your choice is a decent fit in principle, but the commitment to parallelism is only semantic and right now the Rakudo compiler never takes advantage of it, so it will actually run your code sequentially, which presumably isn't a good fit in practice.

Instead I suggest you consider:

  • Using map to distribute an operation over multiple elements (in a shallow manner; map doesn't recursively descend into a deep structure like the hyperoperators, deepmap etc., but that's OK for your use case) ...

  • ... in combination with the race method which parallelizes the method it proceeds.

So you might write:

my @candidate =
  @!repo.hyper.map(*.get: :$package).unique(:with(&[eqv])).flat;

Alternatively, check out task 94 in Using Perl 6.

if the approach mention in the following link is suitable for this use case! https://perl6advent.wordpress.com/2013/12/08/day-08-array-based-objects/

I don't think so. That's about constructing a general purpose container that's like an array but with some differences to the built in Array that are worth baking into a new type.

I can just about imagine such things that are vaguely related to your use case -- eg an array type that automatically hyper distributes method calls invoked on it, if they're defined on Any or Mu (rather than Array or List), i.e. does what I described above but with the code @!repo.get... instead of hyper @!repo.map: *.get .... But would it be worth it (assuming it would work -- I haven't thought about it beyond inventing the idea for this answer)? I doubt it.

More generally...

It seems like what you are looking for is cookbook like material. Perhaps a question posted at the reddit sub /r/perl6 is in order?

like image 137
raiph Avatar answered Oct 17 '22 14:10

raiph