簡體   English   中英

Haskell:列表monad中的變量范圍

[英]Haskell: variable scope in list monad

我正在閱讀關於列表monad的在線Haskell書。 在本書中,列表monad的定義如下:

instance Monad [] where  
    return x = [x]  
    xs >>= f = concat (map f xs)  
    fail _ = []

然后有一個列表monad用法的例子,如下所示:

Prelude> [1,2] >>= \n -> ['a','b'] >>= \ch -> return (n,ch)  
[(1,'a'),(1,'b'),(2,'a'),(2,'b')]

我是Haskell的新手,關於這個例子的問題是,為什么變量'n'在return (n,ch)的lambda表達式中可用。 n在另一個lambda表達式中定義,我不明白為什么在一個lambda表達式中定義的變量在后續的lambda表達式中可用。 我嘗試根據列表monad定義轉換下面的示例:

Prelude> concat (map (\ch -> return (n, ch)) (concat (map (\n -> ['a', 'b']) [1, 2])))

<interactive>:32:29: error: Variable not in scope: n

但是正如你所看到的,我得到一個錯誤,說變量n在另一個lambda表達式的范圍內不可用。 也許這本書只提出了列表monad定義的簡化版本?

[1,2] >>= \n -> ['a','b'] >>= \ch -> return (n,ch)  

未被解析為

[1,2] >>= (\n -> ['a','b']) >>= (\ch -> return (n,ch))

但作為

[1,2] >>= \n -> (['a','b'] >>= (\ch -> return (n,ch)))

您使用concat / map翻譯反映了“錯誤”解析。 我們可以將其改編為正確的。

第一個>>=成為

concat (map (\n -> ???) [1,2])

現在我們可以翻譯內部>>=替換??? 如所須:

??? = concat (map (\ch -> ???2) ['a','b'])
???2= return (n,ch)

結果:

concat (map (\n -> concat (map (\ch -> return (n,ch)) ['a','b'])) [1,2])

因為你的表達:

[1,2] >>= \n -> ['a','b'] >>= \ch -> return (n,ch)

相當於:

--                  body of the lambda expression
--                _________^_______________________
--               /                                 \
[1,2] >>= (\n -> (['a','b'] >>= \ch -> return (n,ch)))
--        \________________ _________________________/
--                         v
--              entire right operand

所以在第一個>>=的右邊你已經寫了一個lambda表達式

[] monad的定義是:

instance Monad [] where  
    return x = [x]  
    xs >>= f = concat (map f xs)

所以你寫了:

[1,2] >>= (\n -> (['a','b'] >>= \ch -> return (n,ch)))
 -> concat (map (\n -> (['a','b'] >>= \ch -> return (n,ch)) [1,2])
 -> concat (map (\n -> concat (map (\ch -> return (n,ch)) ['a','b'])) [1,2])
 -> concat (map (\n -> concat (map (\ch -> [(n,ch)]) ['a','b'])) [1,2])

所以n\\ch -> [(n,ch)]表達式的范圍內。 在最后一個聲明中,我們獲得:

Prelude> concat (map (\n -> concat (map (\ch -> [(n,ch)]) ['a','b'])) [1,2])
[(1,'a'),(1,'b'),(2,'a'),(2,'b')]

那么@chi和@Willem Van Onsem的答案是完美的,但只是為了變化,我想提一下,列表理解只是這個monadic工作的語法糖;

Prelude> [(x,y) | x <- [1,2], y <- ['a', 'b']]
[(1,'a'),(1,'b'),(2,'a'),(2,'b')]

然而,因為列表也是Haskell中應用程序類的實例,所以在不觸及monad實例的情況下完成這項工作的另一個好方法是;

Prelude> (,) <$> [1,2] <*> ['a','b']
[(1,'a'),(1,'b'),(2,'a'),(2,'b')]

我想詳細說明后者,

(,)實際上是一個帶有兩個參數和一個類型簽名的函數; a -> b -> (a,b) <$>fmap的內聯形式。 因此,當我們應用(,) <$> [1,2] ,我們得到的是一個應用函子(函數列表),如[(,) 1, (,) 2] 現在我們可以應用具有類型簽名的applicative運算符<*> Applicative f => f (a -> b) -> fa -> fb在類型簽名中f不應該與函數混淆。 它在這里指定一個恰好是[] (列表)類型的仿函數。 其中, <*>將從函子中解包所包含的函數,並將其應用於提供的函子的包含值,以便以相同的函子類型返回這些應用程序的結果。 很明顯,由於列表應用程序是由所有元素一對一定義的,因此結果證明是所有組合的元組列表。

暫無
暫無

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

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