我正在学习Haskell,目前在Monads here上阅读Dan Piponi的博客。他的前两个例子对我来说非常有意义。但是我无法理解第三个例子,他编写了两个随机函数f
和g
:
f :: a -> StdGen -> (b,StdGen)
g :: a -> StdGen -> (b,StdGen)
我的理解是,g
的返回类型为(b,StdGen)
,但f
分别带有a
和StdGen
类型的两个参数,因此{{1} }不能与f
合成。但是,如果我们打开g
返回的元组并将f x
类型的第二个成员提供给g
,则部分应用的版本g
可以与StdGen
合并。
所以,我认为函数f x
应该使用函数bind
:
f x
并返回一个函数 f x :: StdGen -> (b,StdGen)
,其输入类型与f'
的返回类型相同,即g
:
(b,StdGen)
Bind应该看起来像:
f' :: (b,StdGen) -> (b,StdGen)
但丹将bind :: (StdGen -> (b,StdGen)) -> ((b,StdGen) -> (b,StdGen))
定义为:
bind
从该帖子的评论来看,很明显其他许多人最初都无法遵循丹的推理路线。 Dan也提供了一个示例,但函数bind :: (a → StdGen → (b,StdGen)) → (StdGen → (a,StdGen)) → (StdGen → (b,StdGen))
不使用不同类型addDigit
和a
,而是使用常见类型b
。
有人可以描述Dan试图撰写的函数类型以及他如何使用他正在使用的Int
类型吗?
答案 0 :(得分:4)
不应将f
视为返回(b, StdGen)
的双参数函数,而应将f
视为返回StdGen -> (b, StdGen)
的单参数函数。因此,将整个类型 StdGen -> (b, StdGen)
视为单个块/实体。如果有帮助,请为自己定义一个类型别名:
type Randomized a = StdGen -> (a, StdGen)
现在我们可以写
bind :: (a -> Randomized b) -> (Randomized a -> Randomized b)
并尝试实施它。这种类型遵循先前bind
类型的模式:我们采用一个函数,它接受一个正常的东西并返回一个装饰的东西,然后把它变成一个函数,它接受并返回一个装饰的东西。 (但同样,装饰不只是与StdGen
配对 - 装饰的东西本身就是一个功能。)
同样,第二个练习是实施
unit :: a -> Randomized a
再次遵循之前练习中设置的模式。