Haskell中运算符`>>>`和`>> =`如何工作?
我一直在阅读一个Haskell d3js库:
这是定义Haskell框的代码:
box :: Selector -> (Double,Double) -> St (Var' Selection)
box parent (w,h) = do
assign
$ ((d3Root
>>> select parent
>>> func "append" [PText "svg"]
>>> width w
>>> height h
>>> style "background" "#eef") :: Chain () Selection)
使用d3.js代码中的box
函数实际导出的代码使用>>=
运算符,如下所示:
import Control.Monad
import qualified Data.Text as T
import D3JS
test :: Int -> IO ()
test n = T.writeFile "generated.js" $ reify (box "#div1" (300,300) >>= bars n 300 (Data1D [100,20,80,60,120]))
为避免出现像箭头这样不受欢迎的问题:如何在haskell中使用箭头运算符在哪里可以找到类型签名或其他基本信息? 是否有资源可以进一步了解:
$
Haskell:之间的区别。 (点)和$(美元符号) >>>
这可能是一个箭头,但我不知道我在哪里导入它。 >>=
第一个很容易找到,但答案令人困惑:
*Main Lib> :t ($)
($) :: (a -> b) -> a -> b
我发现f $ abc = f ( (ab) c )
而fabc = (((fa) b) c
Prelude对>>=
涉及monad给出了类似的回应。 就我而言,它可能是IO monad。 或者d3js声明monad St()
*Main Lib> :t (>>=)
(>>=) :: Monad m => m a -> (a -> m b) -> m b
最后一个根本没有出现......这太糟糕了,因为它看起来非常重要
*Main Lib> :t (>>>)
<interactive>:1:1:
Not in scope: ‘>>>’
Perhaps you meant one of these:
‘>>’ (imported from Prelude), ‘>>=’ (imported from Prelude)
最后一次冒着捆绑太多问题的风险。 有人可以解释这种类型的签名? 特别是最后一个项目:
box :: Selector -> (Double,Double) -> St (Var' Selection)
包索引
Hackage上的软件包通常具有定义的所有功能/类型/等的索引页面。 d3js包的索引位于:
您可以通过以下方式到达:
Source | Contents | Index | Style
Source | Contents | Index | Style
Source | Contents | Index | Style
。 点击索引链接。 什么是St
?
St
被定义为
type St r = RWS () Text Int r
RWS
只是一个具有读写器,写入器和状态功能的monad。 Reader环境是()
,它写入Text
值和一个Int
状态。
这是一个方便的定义(可从mtl
包中获得),因此您不必使用ReaderT
, WriterT
和StateT
构建自己的monad变压器堆栈。
请注意,阅读器环境是()
,这意味着库只是真正使用monad的作者和状态方面。
box ... >= box ...
如何box ... >= box ...
工作?
该box
功能有类型:
box :: Selector -> (Double, Double) -> St (Var' Selection)
这意味着它需要一个Select
和一对双打,并在St
单元中返回一个Var' Selector
。
表达式box "#div1" (300,300) >>= bars n 300 (Data1D [...])
与以下内容相同:
do vs <- box "#div1" (300,300)
bars n 300 (Data1D [...]) vs
解释:
box
函数以创建Var' Selection
。 这发生在St
monad。 bars
与Var' Selection
通过第一步返回。 Var' Selection
和Selection
之间有什么区别? 老实说,我不确定。
>>>
运算符
>>>
是一个箭头操作符。 箭头是将计算表达为操作流水线的一种方式。 这个页面上的例子应该让你知道它是如何使用的:
https://www.haskell.org/arrows/syntax.html
请注意, >>>
是根据类型类定义的,所以它的确切作用取决于您使用的类型。 在这种情况下,它与Chain () Selection
值一起使用。
如果你读的文档Chain
(链接),你会看到评论:
链ab的行为就像(a - > b)。 Val Var是链条的起点(=常数),Nil是链条的终止点。
所以这个片段:
-- type:
d3Root >>> select parent -- Chain () Selection
>>> func "append" [PText "svg"] -- Chain a b
>>> width w -- Chain a a
>>> height h -- Chain a a
>>> style ... -- Chain a a
可以理解为:
append
参数"svg"
有趣的是, func
函数的类型是Chain ab
- 它具有不受限制的返回类型b
。 这就是为什么我怀疑最后有明确的类型签名,例如:: Chain () Selection
。
因为@delta已经说过hoogle是你的朋友 - 这里输入的片段来自Control.Category
- 它表示态射的从左到右的组合。
在这种情况下,手头的类别是与monad相关的Kleisli类别,它仅仅是Hask的一个子类别 - 那么>>>
运算符就是缺少的链接,可以使函数
f :: m a -> m b
g :: m b -> m c
彼此适当地工作。 而
>>= :: m a -> (a -> m b) -> m b
将无法做到这一点,专业版的(>>>)
>>> :: (m a -> m b) -> (m b -> m c) -> ma -> m c
会给你h = (f >>> g)
一个正确的输出。
上一篇: How do the operators `>>>` and `>>=` work in Haskell?
下一篇: Can Haskell make distinctions for different kinds of IO