我从编译器的建议中得到了一些看起来很奇怪的东西。
我创建了一个数据类型来表示二进制数,如下所示:
data Bin = Zero | One
我选择将多位二进制数表示为Bin类型的列表,如下所示:
myNum :: [Bin]
myNum = [One, Zero, One, One] -- represents the number 1011
当然,我想以更方便的方式显示我的二进制数字,所以我创建了一个Show实例来为我做这个:
instance Show Bin where
show Zero = "0"
show One = "1"
showList [] = showString ""
showList (x:xs) = showString (show x) . showList xs
此方法有效,print myNum
正确显示1011
。
我在Haskell仍然相当新,这是我第一次使用showList。但这对我来说很有意义。由于showList具有类型[a] -> ShowS
(它本身是[a] -> (String -> String)
的别名),我理解我通过使用函数组合“连接”列表的元素。但编译器建议我使用showList函数并重新定义它:
Warning: Use foldr
Found:
showList [] = showString ""
showList (x : xs) = showString (show x) . showList xs
Why not:
showList xs = foldr ((.) . showString . show) (showString "") xs
而且,一旦我更换了它的建议,它就提出了进一步的建议:
Error: Eta reduce
Found:
showList xs = foldr ((.) . showString . show) (showString "") xs
Why not:
showList = foldr ((.) . showString . show) (showString "")
我理解Eta减少错误,因为通常更喜欢编写无点函数。但我在第一次转换时遇到了麻烦。我看到foldr
的第二个参数是“基本情况”正确的身份,但我很难理解foldr
的第一个参数中发生了什么。所以我有两个问题:
((.) . showString . show)
?例如,我知道(f . g) x
可以重写为f (g x)
。foldr
?答案 0 :(得分:1)
如何重写它如下,首先让我们将组合模式分解为单独的函数
compose :: [a -> a] -> a -> a
compose = foldr (.) id
您可以将其视为列表f : g : h : []
并将[]
替换为id
,
:
.
f . g . h . id
,f . g . h
或 instance Show Bin where
showList = compose . map (showString . show)
。
接下来让我们用它来重写你的例子,让它更清晰
String -> String
现在这比你所拥有的更清晰,即使它在功能上是相同的。事实上,由于GHC可能会融合它,因此甚至可能最终编译成同样的东西(?)。我们正在将每个项目转换为ShowS
或compose
,然后我们将它们全部组合起来。给 showList = appEndo . mconcat . map (Endo . showString . show)
一个名称和类型可以更容易地看到正在发生的事情。
我想这也可以写成
a -> a
这与上述相同,但依赖于mconcat
形成一个幺半群而Endo
概括了将一个幺半群组合成一个的概念。我们需要newtype Endo a = End {appEndo :: a -> a}
位,因为monoid实例实际上是为
{{1}}