[英]“Double” function composition in Haskell
我從編譯器的建議中發現了一些對我來說很奇怪的東西。
我做了一個數據類型來表示二進制數字,如下所示:
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
,並且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 (gx)
。 foldr
的上下文中有什么作用? 如何重寫它,如下所示,首先讓我們將合成模式分解為一個單獨的函數
compose :: [a -> a] -> a -> a
compose = foldr (.) id
您可以將其可視化為獲取列表f : g : h : []
並將[]
替換為id
和:
.
給你留下f . g . h . id
f . g . h . id
f . g . h . id
或f . g . h
f . g . h
f . g . h
。
接下來,讓我們用它來重寫您的示例,使其更清晰易讀
instance Show Bin where
showList = compose . map (showString . show)
現在,盡管功能相同,但它比您現在的要清晰一些。 實際上,由於GHC可能將其融合(?),因此甚至可能以相同的方式編譯。 我們將每個項目轉換為ShowS
String -> String
或ShowS
,然后將它們全部組成。 提供名稱和類型來compose
,可以更輕松地了解正在發生的事情。
我想這也可以寫成
showList = appEndo . mconcat . map (Endo . showString . show)
這與上面的相同,但是依賴於以下事實: mconcat
a -> a
形成一個monoid,而mconcat
推廣了將一個monoid列表組合為一個的想法。 我們需要Endo
位,因為實際上為
newtype Endo a = End {appEndo :: a -> a}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.