如何使用镜片(lenses)连接[Maybe Text]?

3
有没有办法使用镜头来编写以下内容...
[Maybe Text] -> Text

......这可能可以概括为:

(Monoid a, Traversable t) => t a -> a

我试图做的一个具体示例:

[Just "abc", Nothing, Just "def"] -> "abcdef"
[Nothing, Nothing] -> ""

PS:我假设镜头有一些奇特的组合器可以做到这一点。如果我被镜头的酷炫之处所蒙蔽,而这可以通过更简单的组合器轻松实现,请告诉我。


1
你的广义形式难道不是[Maybe Text] -> Maybe Text的概括吗? - Willem Van Onsem
不,实际上是 [Maybe Text] -> Text,对于边缘情况应该默认为 ""(空字符串)。 - Saurabh Nanda
但是你的 (Monoid a, Traversable t) => t a -> a 暗示了你得到了一个 aTraversable(因此是 Maybe b),因此如果 a ~ Maybe b,那么它将返回一个 a,因此是 Maybe b - Willem Van Onsem
我认为我的概括是错误的,在这种情况下。这些示例是我想要的实际行为。 - Saurabh Nanda
3个回答

7

您的(Monoid a, Traversable t) => t a -> a函数可以进一步概括为一个简单的fold :: (Foldable f, Monoid a) => f a -> a

例如:

Prelude> import Data.Foldable
Prelude Data.Foldable> fold [Just "abc", Nothing, Just "def"]
Just "abcdef"

我们可以使用以下方式从Maybe中获取它:
import Data.Foldable(fold, mempty)
import Data.Maybe(maybe)

foldMaybe :: (Foldable f, Monoid a) => f (Maybe a) -> a
foldMaybe = fromMaybe mempty . fold

例如:

Prelude Data.Foldable Data.Maybe> foldMaybe [Just "abc", Nothing, Just "def"]
"abcdef"
Prelude Data.Foldable Data.Maybe> foldMaybe [Nothing] :: String
""

哇 - 我从未意识到Maybe的幺半群实例会这样表现 - Just"abc" <> Just"def" == Just"abcdef" - Saurabh Nanda
这是一个不错的答案,但只是为了好玩,我会等待看看是否有人能够提出一个使用镜头的解决方案。 - Saurabh Nanda
1
@SaurabhNanda:是的,如果aMonoid的实例,那么Maybe a也是Monoid的实例。它将映射为Nothing <> Just x == Just x <> Nothing == Just x,以及Just x <> Just y == Just (x <> y) - Willem Van Onsem
我不知道为什么我期望 <>Maybe 的情况下像 <|> 一样运作。 - Saurabh Nanda

7
作为 Willem 解决方案的一种变体,我更喜欢使用双倍 fold(一个用于列表,另一个用于 Maybe):
> fold . fold $ [Just "abc", Nothing, Just "def"]
"abcdef"

实际上,对于Maybe类型,fold = maybe mempty id = fromMaybe mempty。因此它们是完全相同的。

它的一般类型是:

fold . fold :: (Monoid a, Foldable t1, Foldable t2, Monoid (t1 a)) => t2 (t1 a) -> a

这个适用于[Maybe a]的完美解决方案。


另一个不错的解决方案,由@dfeuer建议如下:

foldMap fold

在这里,fold会移除所有的Just包装器并将Nothing替换为空字符串。然后,foldMap会连接所有生成的字符串。

4
更好的表述是:折叠映射(foldMap)和折叠(fold)。我认为这样会更清晰易懂,没有改变原意。 - dfeuer
@dfeuer 同意。已添加。 - chi
2
如果 p 是一个幺半群同态,那么我们应该有 p . foldMap f = foldMap (p . f)(这是 Ed Kmett 告诉我的一个未声明的自然性法则,由参数性保证)。对于 Maybe,fold 确实是一个幺半群同态。因此,fold @Maybe . foldMap f = foldMap (fold @Maybe . f)。在这种情况下,f = id。 - dfeuer
更好的是,foldMap g 通常是 Maybe、列表和序列的单子态映射,但不适用于集合或映射(即使是“单子态”风格)。 我相信单子态映射保证了 foldMap g 是一个可交换的单子态映射,但请不要引用我说的话。 - dfeuer

4
答案只是一个折叠,所以您可以使用 foldOf 来愚蠢地“使用透镜”。
foldOf :: Monoid a => Fold s a -> s -> a

如果你能找到一个 myFold :: Fold (t a) a,你就可以使用 foldOf 得到:

foldOf myFold :: Monoid a => t a -> a

幸运的是,对于任何一个 Foldable t,我们都可以使用 folded :: Fold (t a) a 访问它。因此,我们可以使用 foldedfoldOf
foldOf :: Monoid a => Fold s a -> s -> a
folded :: Foldable t => Fold (t a) a

foldOf folded :: (Foldable t, Monoid a) => t a -> a

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接