Haskell: 列表 monad 中的变量范围
Haskell: variable scope in list monad
我正在阅读一本关于列表 monad 的在线 Haskell 书籍。在书中,列表 monad 是这样定义的:
instance Monad [] where
return x = [x]
xs >>= f = concat (map f xs)
fail _ = []
然后有一个像这样的 list 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 表达式的范围内不可用。也许这本书只是介绍了 list 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
的翻译反映了 "wrong" 解析。我们可以将其调整为正确的。
第一个>>=
变为
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 中应用函子 class 的实例,在不触及 monad 实例的情况下完成这项工作的另一种好方法是;
Prelude> (,) <$> [1,2] <*> ['a','b']
[(1,'a'),(1,'b'),(2,'a'),(2,'b')]
我想详细说明一下后者,
(,)
实际上是一个带有两个参数和类型签名的函数; a -> b -> (a,b)
。而 <$>
是 fmap
的内联形式。因此,当我们应用 (,) <$> [1,2]
时,我们得到的是一个应用仿函数(函数列表),如 [(,) 1, (,) 2]
。现在我们可以应用具有类型签名 Applicative f => f (a -> b) -> f a -> f b
的应用运算符 <*>
在类型签名中 f
不应与函数混淆。它指定了一个仿函数,这里恰好是 []
(列表)类型。也就是说,<*>
将从仿函数中解包包含的函数,并将其应用于提供的仿函数的包含值,以 return 相同仿函数类型的这些应用程序的结果.很明显,由于列表应用程序是由所有元素一对一定义的,因此结果证明是所有组合的元组列表。
我正在阅读一本关于列表 monad 的在线 Haskell 书籍。在书中,列表 monad 是这样定义的:
instance Monad [] where
return x = [x]
xs >>= f = concat (map f xs)
fail _ = []
然后有一个像这样的 list 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 表达式的范围内不可用。也许这本书只是介绍了 list 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
的翻译反映了 "wrong" 解析。我们可以将其调整为正确的。
第一个>>=
变为
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 中应用函子 class 的实例,在不触及 monad 实例的情况下完成这项工作的另一种好方法是;
Prelude> (,) <$> [1,2] <*> ['a','b']
[(1,'a'),(1,'b'),(2,'a'),(2,'b')]
我想详细说明一下后者,
(,)
实际上是一个带有两个参数和类型签名的函数; a -> b -> (a,b)
。而 <$>
是 fmap
的内联形式。因此,当我们应用 (,) <$> [1,2]
时,我们得到的是一个应用仿函数(函数列表),如 [(,) 1, (,) 2]
。现在我们可以应用具有类型签名 Applicative f => f (a -> b) -> f a -> f b
的应用运算符 <*>
在类型签名中 f
不应与函数混淆。它指定了一个仿函数,这里恰好是 []
(列表)类型。也就是说,<*>
将从仿函数中解包包含的函数,并将其应用于提供的仿函数的包含值,以 return 相同仿函数类型的这些应用程序的结果.很明显,由于列表应用程序是由所有元素一对一定义的,因此结果证明是所有组合的元组列表。