md / for-comprehension-preprocessor
Requires
- pre/plugin: ^0.7.3
- yay/yay: ^0.2
Requires (Dev)
- phpunit/phpunit: ^6.2
This package is not auto-updated.
Last update: 2024-09-18 11:46:25 UTC
README
for-comprenhensions 是某些群组操作的语法糖,这些操作可以在单子集合中找到:withEach
、withFilter
、filter
、map
和 flatMap
。这个预处理器会将使用 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 以及实现
withEach
、withFilter
、filter
、map
和flatMap
的单子集合接口。