[英]Haskell fmap composition misunderstanding
如果我組成兩個fmap
Prelude> :t (fmap.fmap)
(fmap.fmap)
:: (Functor f, Functor f1) => (a -> b) -> f1 (f a) -> f1 (f b)
我得到一個 function,它將 function 應用於 2 個嵌套結構級別f1
和f
內的值。
我可以使用它——這正如我預期的那樣有效:
Prelude> (fmap.fmap) (+1) [[1,2]]
[[2,3]]
正如我預期的那樣使用推斷類型(結果周圍有 2 個結構級別)
Prelude> :t (fmap.fmap) (+1) [[1,2]]
(fmap.fmap) (+1) [[1,2]] :: Num b => [[b]]
以下不起作用。 我也期待這一點(因為我們不能將sum
應用於單個數字):
Prelude> (fmap.fmap) sum [[1,2]]
<interactive>:39:2: error:
• Could not deduce (Num (t0 b))
from the context: (Num (t b), Num b, Foldable t)
bound by the inferred type for ‘it’:
(Num (t b), Num b, Foldable t) => [[b]]
at <interactive>:39:2-24
The type variable ‘t0’ is ambiguous
• In the ambiguity check for the inferred type for ‘it’
To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
When checking the inferred type
it :: forall (t :: * -> *) b.
(Num (t b), Num b, Foldable t) =>
[[b]]
Prelude> :t (fmap.fmap) sum [[1,2]]
(fmap.fmap) sum [[1,2]] :: (Num (t b), Num b, Foldable t) => [[b]]
但! 如果我將一層結構更改為Maybe
類型:
Prelude> (fmap.fmap) sum Just [1,2]
Just 3
然后它開始工作,但在我看來打破了類型簽名(fmap.fmap):: (Functor f, Functor f1) => (a -> b) -> f1 (fa) -> f1 (fb)
(因為它在第一層結構中應用sum
function,而不是我預期的第二層)。
我認為問題在於我理解 function 申請順序如何在這里評估,因為我發現使用括號這在具有可折疊列表值的兩個結構級別內按預期工作(與第一個示例中的數字相比):
Prelude> (fmap.fmap) sum (Just [[1,2],[2,3]])
Just [3,5]
但是這里發生了什么:
Prelude> (fmap.fmap) sum Just [1,2]
Just 3
為什么跳過第一級結構?
這里function申請的順序是什么?
Haskell 是如何推斷最終類型的?
Prelude>:t (fmap.fmap) sum Just [1,2] (fmap.fmap) sum Just [1,2]:: Num t => Maybe t
為什么我理解的Maybe t
而不是Maybe List t
(fmap.fmap)
必須確定f1 (fb)
兩層結構而不是一層?
讓我們計算一下,為了簡單起見,假設數字文字是Int
。
(fmap.fmap) sum Just [1,2]
= fmap (fmap sum) Just [1,2]
| | \ -- an additional argument applied to the result of fmap
| \ -- the value with a type of the form f a with f Functor
\ -- the function to fmap
在這里, Just
是一個 function [Int] -> Maybe [Int]
,所以第一個fmap
對仿函數f = (->) [Int]
進行操作,我們有fmap = (.)
因為這是在Functor ((->) [Int])
。
= (.) (fmap sum) Just [1,2]
= (fmap sum) (Just [1,2])
現在, fmap f (Just x) = Just (fx)
因為這就是Functor Maybe
的定義方式。
= Just (sum [1,2])
= Just 3
- 為什么跳過第一級結構?
它不是。 第一級是(->) [Int]
。
- 這里function申請的順序是什么?
通常的那個。 fmap.fmap
應用於sum
。 結果應用於Just
。 最終結果應用於[1,2]
。
- Haskell 如何推斷最終類型?
它看到Just
是一個“包裹在(->) [Int]
仿函數中的值”,並使用它來實例化第一個fmap
。 第二個fmap
改為在Maybe
級別上使用,因為Just
返回它。
您剛剛發現函數本身就是函子。 該陳述可能聽起來有點令人困惑,所以讓我們進一步探討一下。 讓我們看一下 function 組合運算符(.)
:
Prelude> :t (.)
(.) :: (b -> c) -> (a -> b) -> a -> c
但是現在讓我們稍微重寫類型簽名:
(.) :: (b -> c) -> ((->) a b) -> ((->) a c)
在這里,我表現得好像 function 組合箭頭是一個普通的中綴運算符,並且可以放在看起來像它的“類型參數”(實際上它可以)的前面。 現在我們注意到一些有趣的事情—— (.)
看起來幾乎和fmap
完全一樣:見:
(.) :: (b -> c) -> ((->) a b) -> ((->) a c)
fmap :: Functor f => (b -> c) -> f b -> f c
事實上,可以為部分應用的 function 箭頭(->) r
編寫一個Functor
實例(其中((->) r) a
與(r -> a)
相同):
instance Functor ((->) r) where
fmap = (.)
-- type signature here would be:
-- fmap :: (a -> b) -> ((->) r) a -> ((->) r) b
-- that is:
-- fmap :: (a -> b) -> (r -> a) -> (r -> b)
所以,如果f
和g
是函數,那么fmap fg = f. g
fmap fg = f. g
。
現在,這與您的問題有何關系? 讓我們看一下您感到困惑的表達式:
Prelude> (fmap.fmap) sum Just [1,2]
Just 3
讓我們go一點一點的通過這個。 首先,請注意這可以重寫為: (fmap (fmap sum) Just) [1,2]
。 現在, fmap sum
是一個 function — 這意味着它是一個函子,因此,使用fmap = (.)
作為我上面所說的函數,這變成: ((fmap sum). Just) [1,2]
,即fmap sum (Just [1,2])
。 當然,這只是評估為Just 3
。
感謝您的回答,它為我清除了Just not Just [1,2]作為 (fmap.fmap) 的第二個參數。
我想更詳細地了解類型推斷(如果我錯了請糾正我)
我們有表達式: (fmap.fmap) sum Just [1,2]
(fmap.fmap) 的類型是(a -> b) -> f1 (fa) -> f1 (fb)
這確定它需要兩個下一個 arguments - sum和Just並返回 function。
sum的類型為List int -> int (為簡單起見,int)這意味着a = List int和b = int
第二個參數只是類型a -> Maybe a或其他詞(->) a (Maybe a)所以f1 = (->) a和f = Maybe
這意味着f1 (fb) = (->) (List int) (Maybe int)
或者換句話說表達式的結果類型(fmap.fmap) sum就是List int -> Maybe int所以結果是function。
最后,我們將值 [1,2] 應用到這個 function 並按照推斷類型獲得值 Just 3。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.