使用在超方法链中的gather/take的CALL-ME不起作用。

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

CALL-ME with gather/take used in a hyper method chain doesn't work

问题

在玩弄方法链和 `CALL-ME`
以下是我正在使用的玩具类,`CALL-ME` 只是调用给定的值的 Seq 的 `double` 方法。

```raku
class Math does Callable
{
    method  CALL-ME(Iterable:D $i)
    {
        gather {
            $i.map: -> $v { take self.double($v) } ;
        }
    }

    method double($x) { return $x * 2}
}

my $d = Math.new();

首先检查是否可以在方法链中包含 Math 对象 $d

这段代码

(1,2,3).map( * + 1 ).$d.map( * +1).say;

输出正确的结果

(5 7 9)

现在将 hyper 放入方法链中

(1,2,3).hyper.map( * + 1 ).$d.map( * + 1 ).say;

这次的输出是

在此处启动的并行迭代的工作程序:
  在 block <unit> 中,在 callme.raku 的第 6
终止于:
    在没有 gather 的情况下 take
      在 callme.raku 的第 6 行中的 block

其中第 6 行是 take 行。

我可以通过将 CALL-ME 替换为另一个 map 调用来解决此问题(如下所示),但这引发了一个问题,为什么 map 可以正常工作,而 CALL-ME 不能呢?

(1,2,3).hyper.map( * + 1 ).map( -> $v { $d.double($v) } ).map( * +1).say;

我有遗漏什么吗?

[编辑] 根据 Rawley 的回复更新。

假设我创建一个更大的类,其中还包括一个 CALL-ME。理想情况下,我希望方法链在串行和并发工作流程中都能以最佳方式运行,而用户不必跳过太多步骤。

如果我保留 CALL-ME 中的 gather/take,则在串行上下文中使用时会出现惰性执行,但在使用 hyper 时会完全中断。

或者,删除 CALL-ME 中的 gather/take 意味着我有一个可以在串行和超级上下文中运行的 CALL-ME。不足之处在于串行用例最终以急切模式运行。

有没有一种方法可以通过测试其运行的上下文使 CALL-ME 在两种情况下都能正常工作?类似于下面的假设性 is-hyper-active

    method  CALL-ME(Iterable:D $i)
    {
        if is-hyper-active {
            $i.map: -> $v { take self.double($v)
        } else {
            gather {
                $i.map: -> $v { take self.double($v) } ;
            }
        }
    }

<details>
<summary>英文:</summary>

Been playing with method chaining and `CALL-ME`

Below is a toy class I&#39;m using to play with. The `CALL-ME` just invokes the `double` method for the Seq of values it gets given. 

```raku
class Math does Callable
{
    method  CALL-ME(Iterable:D $i)
    {
        gather {
            $i.map: -&gt; $v { take self.double($v) } ;
        }
    }

    method double($x) { return $x * 2}
}

my $d = Math.new();

First check that it is possible to include the Math object, $d, in a method chain.

This code

(1,2,3).map( * + 1 ).$d.map( * +1).say;

outputs the correct result

(5 7 9)

Now put hyper into the method chain

(1,2,3).hyper.map( * + 1 ).$d.map( * + 1 ).say;

Output this time is

A worker in a parallel iteration (hyper or race) initiated here:
  in block &lt;unit&gt; at callme.raku line 6

Died at:
    take without gather
      in block  at callme.raku line 6

where line 6 is the take line.

I can work around the issue by replacing the CALL-ME with another call to map (as below), but that leaves the question as to why map can do-the-right-thing, but CALL-ME cannot?

(1,2,3).hyper.map( * + 1 ).map( -&gt; $v { $d.double($v) } ).map( * +1).say;

Have I missed something?

[EDIT] Update following reply from Rawley.

Say I create a larger class that also includes a CALL-ME. Ideally I'd like the method chaining to run optimally in both a serial and concurrent workflow without the user having to jump through too many hoops.

If I leave the gather/take in the CALL-ME I get lazy execution when used in a serial context, but it breaks completely when hyper is used.

Alternatively, removing the gather/take means I have a CALL-ME that works in both a serial & hyper context. The downside is the serial use-case ends up running in eager mode.

Is there a way for CALL-ME to have it both ways by testing the context it is running in?

Something like the hypothetical is-hyper-active below

    method  CALL-ME(Iterable:D $i)
    {
        if is-hyper-active {
            $i.map: -&gt; $v { take self.double($v)
        } else {
            gather {
                $i.map: -&gt; $v { take self.double($v) } ;
            }
        }
    }

答案1

得分: 6

Hyper 与传统的 Seq 有很大的不同,使用 gathertake 可能不会起作用,因为 Hyper 是异步的。相反,您可以从您的 CALL-ME 中删除 gathertake,并简单地返回映射的结果来使其工作:

class Math does Callable {
    method CALL-ME(Iterable:D $i) {
        $i.map: -&gt; $t { self.double($t) };
    }

    method double($x) { return $x * 2}
}

现在你应该看到:

(1,2,3).hyper.map( * + 1 ).$d.map( * + 1 ).say;

可以工作。

个人而言,我避免使用 gathertake,更倾向于只使用方法和函数来构建我的 Seq。此外,在不返回值的函数中省略 return 更符合惯用法。

英文:

Hyper is a lot different from traditional Seq, using gather and take most likely won't work because of the asynchronicity of Hyper. Instead you can drop the gather and take from your CALL-ME and simply return the result of the map to make this work:

class Math does Callable {
    method CALL-ME(Iterable:D $i) {
        $i.map: -&gt; $t { self.double($t) };
    }

    method double($x) { return $x * 2}
}

Now you should see that:

(1,2,3).hyper.map( * + 1 ).$d.map( * + 1 ).say;

works.

Personally, I avoid gather and take and prefer to just use methods and functions to construct my Seq's. Also it's a little more idiomatic to omit the return on functions that don't return in multiple places.

huangapple
  • 本文由 发表于 2023年6月19日 01:43:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/76501841.html
匿名

发表评论

匿名网友

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

确定