如何在Raku中注册不同的事件到供应中?

huangapple go评论68阅读模式
英文:

How do I register different events in a supply in Raku?

问题

"Hopefully 'events' isn't a misnomer in Rakuland. As far as I understand, Supplies are the Raku equivalent for 'events' in other programming languages such as NodeJS. In NodeJS, you can register different events which can be targeted specifically with emit's first parameter. Is it possible to do this in Raku?"

const EventEmitter = require('events');
const eventEmitter = new EventEmitter();

eventEmitter.on('start', () => {
  console.log('started');
});

eventEmitter.on('end', (value) => {
  console.log(`ended ${value}`);
});

eventEmitter.emit('start');
eventEmitter.emit('end', 23);

"I'm reading 'A nice supplies: syntactic relief for working with asynchronous data' (which I haven't finished yet) and I see jnthn implements a subscription mechanism with 'subscribe' and 'send' so I'm wondering if that's the way to go about it."

英文:

Hopefully "events" isn't a misnomer in Rakuland. As far as I understand, Supplies are the Raku equivalent for "events" in other programming languages such as NodeJS. In NodeJS, you can register different events which can be targeted specifically with emit's first parameter. Is it possible to do this in Raku?

const EventEmitter = require('events');
const eventEmitter = new EventEmitter();

eventEmitter.on('start', () => {
  console.log('started');
});

eventEmitter.on('end', (value) => {
  console.log(`ended ${value}` );
});

eventEmitter.emit('start');
eventEmitter.emit('end', 23);

I'm reading A nice supplies: syntactic relief for working with asynchronous data (which I haven't finished yet) and I see jnthn implements a subscription mechanism with subscribe and send so I'm wondering if that's the way to go about it.

答案1

得分: 17

A Supply is a suitable mechanism for event distribution, but unlike EventEmitter, it doesn't directly have a named events mechanism - in part because Raku already has plentiful ways to filter and dispatch, which can be reused for the purpose.

The most common approach is to use typed messages:

class Start { }

class End {
    has $.value
}

Given a Supplier, we can emit these:

my $supplier = Supplier.new;
$supplier.emit(Start); # Don't need an instance if there's no data
$supplier.emit(End.new(value => 42));

The receiving end is the Supply:

my $supply = $supplier.Supply;

Typically, the thing doing the emitting keeps the Supplier private and returns the Supply, which only allows receiving.

On the receiving end, we have a multitude of ways to work with the events. For example, we could translate your example most directly as:

$supply.grep(Start).tap: {
    say "started";
}

$supply.grep(End).tap: {
    say "ended {.value}";
}

If using a structured supply or react block that'd be:

react {
    whenever $supply.grep(Start) {
        say "started";
    }
    whenever $supply.grep(End) {
        say "ended {.value}";
    }
}

Or one might prefer to use the when match construct:

$supply.tap: {
    when Start { say "started"; }
    when End { say "ended {.value}"; }
}

Which again works similarly in the structured approach:

react {
    whenever $supply {
        when Start { say "started"; }
        when End { say "ended {.value}"; }
    }
}

Or one could write multiple dispatch routines and subscribe to those:

multi process(Start) {
    say "started";
}
multi process(End (:$value)) { # Can destructure the message in the signature
    say "ended $value";
}
$supply.tap(&process);

Besides the flexibility of approach on the receiving end, another advantage is that typos in class names will be detected at compile time, whereas typos in strings - at least if you're not in TypeScript or something similar - are likely to end up being noticed until rather later.

英文:

A Supply is a suitable mechanism for event distribution, but unlike EventEmitter it doesn't directly have a named events mechanism - in part because Raku already has plentiful ways to filter and dispatch, which can be reused for the purpose.

The most common approach is to use typed messages:

class Start { }

class End {
    has $.value
}

Given a Supplier we can emit these:

my $supplier = Supplier.new;
$supplier.emit(Start); # Don't need an instance if there's no data
$supplier.emit(End.new(value => 42));

The receiving end is the Supply:

my $supply = $supplier.Supply;

Typically, the thing doing the emitting keeps the Supplier private and returns the Supply, which only allows receiving.

On the receiving end, we have a multitude of ways to work with the events. For example, we could translate your example most directly as:

$supply.grep(Start).tap: {
    say "started";
}

$supply.grep(End).tap: {
    say "ended {.value}";
}

If using a structured supply or react block that'd be:

react {
    whenever $supply.grep(Start) {
        say "started";
    }
    whenever $supply.grep(End) {
        say "ended {.value}";
    }
}

Or one might prefer to use the when match construct:

$supply.tap: {
    when Start { say "started"; }
    when End { say "ended {.value}"; }
}

Which again works similarly in the structured approach:

react {
    whenever $supply {
        when Start { say "started"; }
        when End { say "ended {.value}"; }
    }
}

Or one could write multiple dispatch routines and subscribe those:

multi process(Start) {
    say "started"
}
multi process(End (:$value)) { # Can destructure the message in the signature
    say "ended $value"
}
$supply.tap(&process);

Besides the flexibility of approach on the receiving end, another advantage is that typos in class names will be detected at compile time, whereas typos in strings - at least if you're not in TypeScript or something similar - are likely to end up being noticed until rather later.

huangapple
  • 本文由 发表于 2023年4月19日 22:10:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/76055530.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定