我对 (->)
很感兴趣当我查找有关 (->)
的信息时在 ghci 中。它说,
data (->) a b -- Defined in `GHC.Prim`
到目前为止一切都很好,但是当它说 -
instance Monad ((->) r) -- Defined in `GHC.Base`
instance Functor ((->) r) -- Defined in `GHC.Base`
这意味着什么?为什么 GHC 将其定义为 Monad 的实例,而 Functor 定义为
(->)
?
最佳答案
一开始可能有点困惑,但要记住的一个重要概念是 (->)
不是单子(monad)或仿函数,而是 (->) r
是。 Monad
和 Functor
类型都有 * -> *
,所以他们只期望一个类型参数。
这意味着 fmap
对于 (->) r
好像
fmap g func = \x -> g (func x)
这也被称为
fmap g func = g . func
这只是正常的功能组合!当你
fmap g
超过 func
,您可以通过应用 g
来更改输出类型给它。在这种情况下,如果 func
类型为 a -> b
, g
必须有类似 b -> c
的类型.Monad
实例更有趣。它允许您在应用程序发生“之前”使用函数应用程序的结果。帮助我理解的是看到一个例子f :: Double -> (Double,Double)
f = do
x1 <- (2*)
x2 <- (2+)
return (x1, x2)
> f 1.0
(2.0, 3.0)
这样做是将隐式参数应用于
f
绑定(bind)到右侧的每个函数。所以如果你传入 1.0
至f
,它将绑定(bind)值 2 * 1.0
至x1
并绑定(bind) 2 + 1.0
至x2
,然后返回 (x1, x2)
.它确实很容易将单个参数应用于许多子表达式。这个函数相当于f' x = (2 * x, 2 + x)
为什么这很有用?一种常见的用法是
Reader
monad,它只是 (->) r
的新类型包装器. Reader
monad 可以很容易地在应用程序中应用静态全局配置。你可以编写如下代码myApp :: Reader Config ()
myApp = do
config <- ask
-- Use config here
return ()
然后你用
runReader myApp initialConfig
运行你的应用程序。 .您可以在 Reader Config
中轻松编写操作monad,组合它们,将它们链接在一起,它们都可以访问全局的只读配置。另外还有一个小伙伴ReaderT
monad 转换器,它允许您将其构建到您的转换器堆栈中,让您拥有可以轻松访问静态配置的非常复杂的应用程序。
关于haskell - 被 (->) 作为 monad 和 functor 的实例所吸引,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21026021/