这个 Haskell 代码是如何工作的? fmap fmap (,) <*> 只是 .撤销

How does this Haskell code work? fmap fmap (,) <*> Just . reverse

有人可以帮助 explain/breakdown 以下代码:

λ> fmap fmap (,) <*> Just . reverse $ "Whosebug"
Just ("Whosebug","wolfrevokcats")

如果有人能引导我完成他们理解此类代码机制的过程,那就太好了。

让我们尝试解码这种毫无意义的风格混淆:

fmap fmap (,) <*> Just . reverse $ "Whosebug"

第一步:这是如何解析的?好吧,$ 的优先级较低,所以它是

(fmap fmap (,) <*> Just . reverse) $ "Whosebug"

此外,我猜中缀 <*> 的优先级低于 .(可以使用文档或 GHCi 中的 :i 进行检查)。所以,我们得到

((fmap fmap (,)) <*> (Just . reverse)) $ "Whosebug"

让我们减少 $

((fmap fmap (,)) <*> (Just . reverse)) "Whosebug"

因此,<*> 的结果是一个函数。这意味着我们正在 (->) a 应用程序中工作。在那里,(<*>) = \x y z -> x z (y z) 也就是来自 lambda 演算的 S 组合子。我们可以减少它:

(\z -> (fmap fmap (,)) z ((Just . reverse) z)) "Whosebug"

更多测试版,尽可能删除括号:

fmap fmap (,) "Whosebug" ((Just . reverse) "Whosebug")

进一步简化:

fmap fmap (,) "Whosebug" (Just (reverse "Whosebug"))
fmap fmap (,) "Whosebug" (Just "wolfrevokcats")

现在,fmap fmap (,) 部分。 (,) 参数是一个函数,所以这意味着第一个 fmap(->) a 仿函数中工作。在那里,fmap = (.)。所以,这只是对

的混淆
(.) fmap (,) "Whosebug" (Just "wolfrevokcats")
(fmap . (,)) "Whosebug" (Just "wolfrevokcats")
(fmap ((,) "Whosebug")) (Just "wolfrevokcats")
fmap ((,) "Whosebug") (Just "wolfrevokcats")

现在,fmap 的第二个参数是 Maybe String 类型,因此 fmapMaybe 函子中工作。在那里,fmap f (Just x) = Just (f x)。我们得到

Just (((,) "Whosebug") "wolfrevokcats")
Just ((,) "Whosebug" "wolfrevokcats")
Just ("Whosebug", "wolfrevokcats")

结论:无意义的代码往往是无意义的。为了解码这个片段,我们必须 运行 头脑中的类型推断器,回忆 三个 特定实例(即使它们非常标准),并处理优先级。除非故意混淆,否则任何人都不应该使用这种风格。 (当然不是招聘难题!)