好吧,所以我不是一个Haskell程序员,但是我对Haskell背后的许多想法非常感兴趣,并且正在研究学习它。但我被困在了第一步:我似乎不能把头绕在单子上,这似乎是相当基本的。我知道有很多问题需要解释,所以我会更具体地解释困扰我的是什么:
我读了这篇优秀的文章(an introduction in Javascript),并认为我完全理解Monad。然后我在单子上阅读了维基百科的条目,并看到了以下内容:
多态型(M )→(t→M)→(M)的绑定操作,Haskell由infix运算符>>=表示。它的第一个参数是一个一元类型的值,第二个参数是从第一个参数的底层类型映射到另一个一元类型的函数,其结果是另一个一元类型。
好的,在我引用的那篇文章中,bind是一个函数,只需要一个论点。维基百科说有两个。我认为我对单簧管的理解如下:
Monad的目的是接收具有不同输入和输出类型的函数,并使其可组合。它通过将输入和输出类型包装成一个单一的一元类型来实现这一点。
。
但肯定有问题,因为我的绑定概念有一个论点:函数。但是(根据维基百科)Haskell的绑定实际上有两个论点!我的错误在哪里?
发布于 2011-11-02 02:29:30
你不是在犯错误。这里要理解的关键思想是,有两个参数的Haskell函数可以用两种方式来理解。第一个是两个参数的函数。例如,如果您有(+),这通常被看作是接受两个参数并添加它们。另一种看法是,它是一个加法机生产者。(+)是一个函数,它接受一个数字,比如x,并生成一个将添加x的函数。
(+) x = \y -> x + y
(+) x y = (\y -> x + y) y = x + y在处理monads时,有时可能会更好,就像上面提到的=<<,>>=的翻转版本。有两种方法来看待这个问题:
(=<<) :: (a -> m b) -> m a -> m b这是两个参数的函数,
(=<<) :: (a -> m b) -> (m a -> m b)它将输入函数转换为文章中提到的容易组合的版本。正如我前面所解释的,它们是等价的,就像(+)。
发布于 2011-11-02 06:47:09
请允许我推翻你对Monad的信仰。我真诚地希望你认识到,我并不是想变得粗鲁,我只是想尽量避免言简意赅。
Monad的目的是接收具有不同输入和输出类型的函数,并使其可组合。它通过用单一的一元类型包装输入和输出类型来实现这一点。
不完全同意。当你用"A Monad的目的“开头一个句子时,你已经错了。单簧管不一定有“目的”。Monad只是一种抽象,一种适用于某些类型而不是其他类型的分类。Monad抽象的目的很简单,抽象。
Monad由两个相互关联的函数组成: bind和unit。
是也不是。bind和unit的组合足以定义Monad,但是join、fmap和unit的组合同样足够。事实上,后者是单子在范畴理论中的典型描述方式。
Bind接受一个不可组合的函数f,并返回一个接受一元类型作为输入并返回一元类型的新函数g。
再说一遍,不完全是。一元函数f :: a -> m b是完全可组合的,具有某些类型。我可以用函数g :: m b -> c编写它以获得g . f :: a -> c,也可以用函数h :: c -> a预组合它以获得f . h :: c -> m b。
但第二部分是完全正确的:(>>= f) :: m a -> m b。正如其他人所注意到的,Haskell的bind函数以相反的顺序接受参数。
g是可合成的。
嗯,是的。如果是g :: m a -> m b,那么您可以使用函数f :: c -> m a预组合它以获得g . f :: c -> m b,也可以将其与函数h :: m b -> c进行后组合以获得h . g :: m a -> c。请注意,c可以是表单m v,其中m是Monad。我想,当你说“可合成”时,你的意思是“你可以合成这种形式的任意长的函数链”,这有点道理。
单元函数接受f所期望的类型的参数,并将其包装为一元类型。
一种迂回的表达方式,但是是的,那是正确的。
--这是将
unit应用于某个值的结果,然后可以传递给g,或者传递给像g这样的函数的任何组合。
再说一次,是的。虽然调用unit (或在Haskell,return中)然后将其传递给(>>= f)通常不是惯用的Haskell。
-- instead of
return x >>= f >>= g
-- simply go with
f x >>= g
-- instead of
\x -> return x >>= f >>= g
-- simply go with
f >=> g
-- or
g <=< f发布于 2011-11-02 02:58:54
您所链接的文章基于sigfpe的文章,该文章使用了bind的翻转定义:
第一件事是,我翻转了
bind的定义,把它写成'bind‘这个词,而它通常被写成运算符>>=。所以bind f x通常被写成x >>= f。
因此,Haskell bind接受一个包含在单变量中的值,并返回一个函数,该函数接受一个函数,然后用提取的值调用它。我可能在使用不精确的术语,所以使用代码可能更好。
你有:
sine x = (sin x, "sine was called.")
cube x = (x * x * x, "cube was called.")现在,翻译您的JS (Haskell完成自动运行,因此调用bind f返回一个接受元组的函数,然后模式匹配负责将它解压到x和s中,我希望这是可以理解的):
bind f (x, s) = (y, s ++ t)
where (y, t) = f x你可以看到它起作用了:
*Main> :t sine
sine :: Floating t => t -> (t, [Char])
*Main> :t bind sine
bind sine :: Floating t1 => (t1, [Char]) -> (t1, [Char])
*Main> (bind sine . bind cube) (3, "")
(0.956375928404503,"cube was called.sine was called.")现在,让我们来反转bind的参数
bind' (x, s) f = (y, s ++ t)
where (y, t) = f x您可以清楚地看到,它仍然在执行相同的任务,但语法略有不同:
*Main> bind' (bind' (3, "") cube) sine
(0.956375928404503,"cube was called.sine was called.")现在,Haskell有了一个语法技巧,允许您使用任何函数作为infix操作符。所以你可以写:
*Main> (3, "") `bind'` cube `bind'` sine
(0.956375928404503,"cube was called.sine was called.")现在将bind'重命名为>>= ((3, "") >>= cube >>= sine),您已经得到了所需的内容。正如您所看到的,使用这个定义,您可以有效地去除单独的复合运算符。
将新事物转换回JavaScript会产生类似这样的结果(请注意,我只颠倒了参数顺序):
var bind = function(tuple) {
return function(f) {
var x = tuple[0],
s = tuple[1],
fx = f(x),
y = fx[0],
t = fx[1];
return [y, s + t];
};
};
// ugly, but it's JS, after all
var f = function(x) { return bind(bind(x)(cube))(sine); }
f([3, ""]); // [0.956375928404503, "cube was called.sine was called."]希望这会有所帮助,而不是带来更多的混乱--关键是这两个绑定定义是等价的,只是在调用语法上有所不同。
https://stackoverflow.com/questions/7975022
复制相似问题