2015-09-01 50 views
11

joinbind一起定義以將組合數據結構扁平化爲單個結構。有沒有直覺瞭解Monad中的連接兩個函數?

從類型的系統視圖,(+) 7 :: Num a => a -> a可被視爲一個Functor(+) :: Num a => a -> a -> a可以被視爲一個FunctorFunctor,如何獲得一些關於它的直覺,而不是僅僅依靠類型系統?爲什麼join (+) 7 === 14

儘管可以通過函數綁定過程手動步進來獲得最終結果,但如果給出了一些直覺,那將是非常好的。

這是從NICTA練習。

-- | Binds a function on the reader ((->) t). 
-- 
-- >>> ((*) =<< (+10)) 7 
-- 119 
instance Bind ((->) t) where 
    (=<<) :: 
    (a -> ((->) t b)) 
    -> ((->) t a) 
    -> ((->) t b) 
    (f =<< a) t = 
    f (a t) t 

-- | Flattens a combined structure to a single structure. 
-- 
-- >>> join (+) 7 
-- 14 
join :: 
    Bind f => 
    f (f a) 
    -> f a 
join f = 
    id =<< f 

*Course.State> :t join (+) 
join (+) :: Num a => a -> a 
*Course.State> :t join 
join :: Bind f => f (f a) -> f a 
*Course.State> :t (+) 
(+) :: Num a => a -> a -> a 
+7

「'(+)7 :: Num a => a - > a'可以被認爲是一個'Functor' ...」請不要說這樣的話。 '(7+)'不是一個函子,就像說碳原子是鑽石一樣。函子是'(Int - >)',即類型構造函數,它接受類型並從數字到該類型生成函數。 – leftaroundabout

+0

對於函數,'join f = \ a - > f a a'。因此'join(+)= \ a - > a + a'。 – AJFarmar

+0

@leftaroundabout不是'(+)7 :: Num a => a - > a'是類別'Num'上的函數嗎? – Kamel

回答

9

如何得到它的一些直覺而不是僅僅依賴於類型系統?

我寧願說依靠類型系統是建立特定類型直覺的好方法。該類型的join是:

join :: Monad m => m (m a) -> m a 

專門到(->) r,就變成:

(r -> (r -> a)) -> (r -> a) 

現在,讓我們嘗試定義join的功能:

-- join :: (r -> (r -> a)) -> (r -> a) 
join f = -- etc. 

我們知道結果一定是r -> a功能:

join f = \x -> -- etc. 

但是,我們完全不知道什麼是ra類型,因此我們對f :: r -> (r -> a)x :: r沒有任何特別的瞭解。我們的無知意味着有字面上的只有一件事,我們可以與他們無關:通過x作爲參數,既ff x

join f = \x -> f x x 

因此,join的功能通過相同的參數兩次,因爲這是唯一的可能的實施。當然,實施只是一個合適的單子join因爲它遵循的單子法律:

join . fmap join = join . join 
join . fmap return = id 
join . return = id 

驗證是否可能是另外一個不錯的鍛鍊。

7

按照傳統的monad類比作爲計算上下文,join是一種組合上下文的方法。我們從你的例子開始。 join (+) 7。使用函數作爲monad意味着讀者monad。 (+ 1)是一個讀取器monad,它會接收環境併爲其添加一個。因此,(+)將是讀卡器monad內的讀卡器monad。外部閱讀器monad採用環境n並返回形式爲(n +)的閱讀器,這將採用新的環境。 join只是簡單地將兩種環境組合在一起,以便您提供一次,並且它將應用給定的參數兩次。 join (+) === \x -> (+) x x

現在,更一般地,讓我們看看其他一些例子。monad表示潛在的故障。值Nothing是失敗的計算,而Just x是成功的。 Maybe內的Maybe是可能失敗兩次的計算。值爲Just (Just x)顯然是成功的,所以加入後會產生Just x。 A NothingJust Nothing指示在某一時刻失敗,因此加入可能的失敗應指示計算失敗,即Nothing

相似的類比可以爲列表單子,爲此join僅僅concat,作家單子,它使用操作人員monoidal到<>的輸出值中的問題相結合,或者任何其它單子製成。

join是monads的一個基本屬性,並且是使它比函數或應用函數明顯更強的操作。函子可以映射,應用可以是序列,單子可以合併。一般來說,monad通常定義爲joinreturn。恰巧在Haskell中我們發現用return,(>>=)fmap來定義它更方便,但是這兩個定義已被證明是同義的。

+0

'+1'是一個閱讀器monad?你的意思是不知何故在概念上還是在類型層面? –

+0

'(+ 1)'是一個接受參數並添加一個參數的函數。從概念上講,它可以被看作是一個值,它取決於它目前還不知道的只讀值。 '(+ 1)'可以被看作是一個整數,除非我們給它一個「環境」,否則我們無法檢查。 –

+0

所以它只是一個概念上的類比而不是類型層面的事實。 –

2

一個關於join的直覺就是南瓜 2個容器合爲一個。 .e.g

join [[1]] => [1] 
join (Just (Just 1)) => 1 
join (a christmas tree decorated with small cristmas tree) => a cristmas tree 

等等

現在,你怎麼能加入的功能呢?其實功能,可以看作是一個容器。 例如,如果您查看哈希表。你給一把鑰匙,你會得到一個價值(或不)。這是一個函數key -> value(或者如果你更喜歡key -> Maybe value)。 那麼你將如何加入2個HashMap?

比方說,我有(在Python風格)h={"a": {"a": 1, "b": 2}, "b" : {"a" : 10, "b" : 20 }}我該如何加入它,或者如果你更喜歡扁平它? 鑑於"a"我應該得到哪個價值? h["a"]給我{"a":1, "b":2}。我唯一能做的就是再次在這個新值中找到「a」,這給了我1。因此join h等於{"a":1, "b":20}

功能相同。

相關問題