簡體   English   中英

為什么加入。 (翻轉fmap)有類型((A - > B) - > A) - >(A - > B) - > B?

[英]Why does join . (flip fmap) have type ((A -> B) -> A) -> (A -> B) -> B?

有些人在ghci玩弄着仿函數和monad,這讓我得到了一個我希望更好理解其類型和行為的價值。

\\x -> join . x的類型\\x -> join . x \\x -> join . x(Monad m) => (a -> m (mb)) -> (a -> mb)\\y -> y . (flip fmap)的類型\\y -> y . (flip fmap) \\y -> y . (flip fmap)(Functor f) => ((a -> b) -> fb) -> (fa -> c)

ghci版本8.2.2允許定義h = join . (flip fmap) h = join . (flip fmap)

為什么h有類型((A -> B) -> A) -> (A -> B) -> B

特別是,為什么仿函數和monad約束消失了? 這真的是正確和預期的行為嗎? 作為后續行動,我還想問:

為什么評估整數uv h (\\f -> fu) (\\x -> x + v)在每種情況下給出u + 2v

簡而言之 :由於類型推導,Haskell知道mf實際上是部分實例化的箭頭。

派生類型

好吧,讓我們做數學。 函數join . (flip fmap) join . (flip fmap)基本上是你給定的lambda表達式\\x -> join . x \\x -> join . x帶有參數(flip fmap) ,所以:

h = (\x -> join . x) (flip fmap)

現在lambda表達式有類型:

(\x -> join . x) :: Monad m =>   (a -> m (m b)) -> (a -> m b)

現在參數flip fmap有類型:

flip fmap        :: Functor f => f c -> ((c -> d) -> f d)

(我們這里使用cd而不是ab來避免兩種可能不同類型之間的混淆)。

這意味着flip fmap的類型與lambda表達式的參數類型相同,因此我們知道:

  Monad m =>   a   -> m (m b)
~ Functor f => f c -> ((c -> d) -> f d)
---------------------------------------
a ~ f c, m (m b) ~ ((c -> d) -> f d)

所以我們現在知道afc具有相同的類型(這是波浪號的含義~ )。

但我們必須做一些額外的計算:

  Monad m =>   m (m b)
~ Functor f => ((c -> d) -> f d)
--------------------------------
m ~ (->) (c -> d), m b ~ f d

因此我們知道m(->) (c -> d) (基本上這是一個我們知道輸入類型的函數,這里(c -> d) ,輸出類型是m的類型參數。

所以這意味着mb ~ (c -> d) -> b ~ fd ,所以這意味着f ~ (->) (c -> d)b ~ d 另外一個結果是,因為a ~ fc ,我們知道a ~ (c -> d) -> c

所以列出我們得到的東西:

f ~ m
m ~ (->) (c -> d)
b ~ d
a ~ (c -> d) -> c

所以我們現在可以“專門化”我們的lambda表達式和我們的flip fmap函數的類型:

(\x -> join . x)
    :: (((c -> d) -> c) -> (c -> d) -> (c -> d) -> d) -> ((c -> d) -> c) -> (c -> d) -> d
flip fmap
    ::  ((c -> d) -> c) -> (c -> d) -> (c -> d) -> d

flip fmap的類型現在完全匹配lambda表達式的參數類型。 所以(\\x -> join . x) (flip fmap)的類型(\\x -> join . x) (flip fmap)是lambda表達式類型的結果類型,即:

(\x -> join . x) (flip fmap)
    :: ((c -> d) -> c) -> (c -> d) -> d

但是現在我們當然還沒有獲得這個功能的實現。 然而,我們已經向前邁進了一步。

推導實施

既然我們現在知道m ~ (->) (c -> d) ,我們知道應該查找monad箭頭實例

 instance Monad ((->) r) where f >>= k = \\ r -> k (fr) r 

因此對於給定函數f :: r -> a ,作為左操作數,以及函數k :: a -> (r -> b) ~ a -> r -> b作為操作數,我們構造一個新的映射函數應用於f的變量xk應用於xx 因此,這是一種對輸入變量x執行某種預處理的方法,然后在考慮預處理和原始視圖的情況下進行處理(這是人類讀者可以使用解釋)。

現在join :: Monad m => m (ma) -> ma 實現為

 join :: Monad m => m (ma) -> ma join x = x >>= id 

所以對於(->) r monad,這意味着我們將其實現為:

-- specialized for `m ~ (->) a
join f = \r -> id (f r) r

由於id :: a -> a (標識函數)返回其參數,我們可以進一步簡化它:

-- specialized for `m ~ (->) a
join f = \r -> (f r) r

或清潔:

-- specialized for `m ~ (->) a
join f x = f x x

所以它基本上給出了一個函數f ,然后將該參數兩次應用於該函數。

此外,我們知道箭頭類型的Functor實例定義為

 instance Functor ((->) r) where fmap = (.) 

因此它基本上用作函數結果的“后處理器”:我們構造一個新函數,用於使用給定函數進行后處理。

所以現在我們將這個函數專門用於給定的Functor / Monad ,我們可以將實現派生為:

-- alternative implementation
h = (.) (\f x -> f x x) (flip (.))

或者使用更多的lambda表達式:

h = \a -> (\f x -> f x x) ((flip (.)) a)

我們現在可以進一步專注於:

h = \a -> (\f x -> f x x) ((\y z -> z . y) a)

-- apply a in the lambda expression
h = \a -> (\f x -> f x x) (\z -> z . a)

-- apply (\z -> z . a) in the first lambda expression
h = \a -> (\x -> (\z -> z . a) x x)

-- cleaning syntax
h a = (\x -> (\z -> z . a) x x)

-- cleaning syntax
h a x = (\z -> z . a) x x

-- apply lambda expression
h a x = (x . a) x

-- remove the (.) part
h a x = x (a x)

所以h基本上需要兩個參數: ax ,然后將其與執行功能的應用程序a作為函數和x為參數,輸出被傳遞給x功能一次。

樣品用法

作為樣本用法,您使用:

h (\f -> f u) (\x -> x + v)

還是更好的:

h (\f -> f u) (+v)

所以我們可以這樣分析:

   h (\f -> f u) (+v)
-> (+v) ((\f -> f u) (+v))
-> (+v) ((+v) u)
-> (+v) (u+v)
-> ((u+v)+v)

所以我們將u+v添加到v

使用>>> 可以更輕松地排列類型:

                          a   ->     b                       >>>
                                     b              -> c     ::
                          a   ->                       c   

在這里,我們有

join . flip fmap  ==  flip fmap >>> join

flip fmap :: Functor f => f a -> ((a -> b) -> f b )
join      :: Monad   m =>        (m          (m b)) -> m b
----------------------------------------------------------
flip fmap >>> join ::
 (Functor f, Monad m) =>  f a  ->                      m b   , ((a -> b) ->) ~ m, f ~ m
                   ::
 (Functor f, Monad f) =>  f a  ->                      f b   , f ~ ((a -> b) ->)
                   ::    ((a -> b) -> a) -> ((a -> b) -> b)

簡單,機械,世俗。


看看會發生什么 ,組合子樣式定義通常是最容易給擺弄,

(join . flip fmap) f g x =
 join (flip fmap f) g x =          -- join f x = f x x
 (`fmap` f) g g x =                -- f `fmap` g = f . g
 (g . f) g x 
 g (f g) x 

所以我們畢竟不需要x (或者我們呢?)。 函數的joinfmap定義在邊距中給出。 我們到了

(join . flip fmap) f g = g (f g)   -- f :: (a -> b) -> a,  g :: a -> b 
                                   -- f g :: a  , g (f g) :: b

另一種方式是從類型開始,按照modus ponens的規則,

            ((a -> b) -> a)    (a -> b)        --       f  g
            ---------------------------
(a -> b)                 a                     --  g   (f  g)
---------------------------------------
      b

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM