md/for-comprehension-preprocessor

安装: 10

依赖项: 0

建议者: 0

安全: 0

星级: 3

关注者: 1

分支: 0

开放问题: 0

语言:Ruby

类型:预宏

0.1.1 2020-03-10 13:56 UTC

This package is not auto-updated.

Last update: 2024-09-18 11:46:25 UTC


README

for-comprenhensions 是某些群组操作的语法糖,这些操作可以在单子集合中找到:withEachwithFilterfiltermapflatMap。这个预处理器会将使用 for-comprenhension 语法的操作翻译成纯 PHP。这不会在运行时发生,所以不会影响性能。预处理器在 pre 插件和 yay 宏库 之上工作。

让我们看看它在实践中如何工作,包括迭代、过滤、映射和平铺。

迭代

for ($a <- ImmList(1, 2, 3)) { echo $a; }

被翻译为

ImmList(1, 2, 3)->withEach(function ($a) {
    echo $a;
});

这会打印

123

好吧,这很无聊。PHP foreach 也做同样的事情。或者不是吗?使用这种语法,你可以一次执行多个迭代。

for ($a <- ImmList(1, 2, 3); $b <- ImmList($a); $c <- ImmList($b)) { echo $a + $b + $c; }

这被翻译为

ImmList(1, 2, 3)->withEach(function($a) {
    ImmList($a)->withEach(function($b) use ($a) {
        ImmList($b)->withEach(function($c) use ($a, $b) {
            echo ($a + $b + $c) . "\n";
        });
    });
});

并打印

3
6
9

嗯!开始变得有趣了,对吧?哦!但这还不是真正的。

映射

当使用 yield 关键字时,一行 for-comprenhensions 被翻译成映射

for ($a <- ImmList(1, 2, 3)) yield $a + 1;

这变成了

ImmList(1, 2, 3)->map(function() {
    return $a + 1;
});

并产生一个 ImmList(2, 3, 4)

平铺

你可以组合这些表达式来创建更大的表达式。当你向 for-comprenhension 添加行时,它们会被翻译成 flatMap 操作,只有最后一个保持为 map

for {
    $a <- Some(42)
    $b <- Some($a + 1)
    $c <- Some($b + $a + 3)
} yield $c;

这被翻译成

Some(42)->flatMap(function ($a) {
    return Some($a + 1)->flatMap(function ($b) use ($a) {
        return Some($b + $a + 3)->map(function ($c) use ($a, $b) {
            return $c;
        });
    });
});

这实际上是函数式编程中一个非常常见的模式,并且是保持操作在纯上下文中的一种非常好的方式。下面的代码没有任何副作用,并描述了在函数式程序中的 IO 操作。

for {
    $line <- IO\readline()
    _ <- IO\write($line, '/tmp/some_file.txt')
    _ <- IO\printLn("You have successfully written to file")
} yield ()

注意,你可以使用通配符 _ 来忽略操作的返回值。yield () 将返回一个 IO<Unit>。一个 unit 是一个空积。如果你对这个不熟悉,只需把它看作一个 void。它不是,但现在可以这样理解。

过滤

当我们向 for comprenhension 添加操作时,我们可以添加过滤操作,这些过滤操作将作用于这些操作的结果,限制传递给下一个操作的内容。

for {
    $a <- ImmList(1, 2, 3) if $a % 2 == 0
} yield $a;

这段代码被翻译成

ImmList(1, 2, 3)->withFilter(function ($a) {
    return $a % 2 == 0;
})->map(function ($a) {
    return $a;
});

结果是一个新的 List,其中只包含满足谓词 $a % 2 == 0 或换句话说,是偶数的元素。

一个更复杂的例子

for {
    $a <- ImmList(1, 2, 3) if $a % 2 == 0
    $b <- ImmList(1, 2, 3) if $b % 2 != 0
} yield ($a, $b);

这被翻译成

ImmList(1, 2, 3)->withFilter(function ($a) {
    return $a % 2 == 0;
})->flatMap(function ($a) {
    return ImmList(1, 2, 3)->withFilter(function ($b) use ($a) {
        return $b % 2 != 0;
    })->map(function ($b) use ($a) {
        return Pair($a, $b);
    });
});

我们也可以在下一行添加过滤器

for {
    $a <- ImmList(1, 2, 3)
    $b <- ImmList(1, 2, 3)
    if $b % 2 != 0
} yield ($a, $b);

这相当于在上一行旁边添加过滤器。

ImmList(1, 2, 3)->flatMap(function ($a) {
    return ImmList(1, 2, 3)->withFilter(function ($b) use ($a) {
        return $b % 2 != 0;
    })->map(function ($b) use ($a) {
        return Pair($a, $b);
    });
});

注意,预处理器识别元组语法,因此你可以返回元组并将它们分配给多个变量

for {
    ($a) <- Some(Tuple(1))
    ($b, $c) <- Some(Pair(1, 2))
} yield $b;

这被翻译成

Some(Tuple(1))->flatMap(function ($t2) {
    $a = $t2->_1;
    return Some(Pair(1, 2))->map(function ($t1) use ($a) {
        $b = $t1->_1;
        $c = $t1->_2;
        return $b;
    });
});

或者你可以返回元组

for {
    $a <- Some(42)
    $b <- Some(43)
} yield ($a, $b);

它映射到 Some(Pair(42,43))

最后的话

  • 这个预处理器是 alpha 软件。不要在生产环境中使用它。
  • 你不需要使用像 Phunkie 这样的库来使用这个预处理器,但你需要提供 Tuples 以及实现 withEachwithFilterfiltermapflatMap 的单子集合接口。