How do the operators `>>>` and `>>=` work in Haskell?
I have been reading through a Haskell d3js library:
This is the code defining Haskell box:
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)
The code actually exporting using the box
function in d3.js code uses the >>=
operator like this:
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]))
To avoid being like this unpopular question on arrows: How to use arrow operators in haskell Where can I find the type signatures, or other basic information? Are there resources where I can learn more about:
$
Haskell: difference between . (dot) and $ (dollar sign) >>>
this could be an arrow but I don't see where I imported it. >>=
The first one was easy to find but the answer was confusing:
*Main Lib> :t ($)
($) :: (a -> b) -> a -> b
I found that f $ abc = f ( (ab) c )
while fabc = (((fa) b) c
Prelude gives a similar response for >>=
involving monads. In my case, it might be the IO monad. Or the d3js statement monad St()
*Main Lib> :t (>>=)
(>>=) :: Monad m => m a -> (a -> m b) -> m b
The last one doesn't show up at all... which is too bad because it looks pretty essential
*Main Lib> :t (>>>)
<interactive>:1:1:
Not in scope: ‘>>>’
Perhaps you meant one of these:
‘>>’ (imported from Prelude), ‘>>=’ (imported from Prelude)
Lastly at the risk of bundling too many questions at once. Can someone explain this type signature? Especially the last item:
box :: Selector -> (Double,Double) -> St (Var' Selection)
The package Index
Packages on Hackage usually have an index page of all functions / types / etc. defined. The index for the d3js package is located here:
You can get there by:
Source | Contents | Index | Style
Source | Contents | Index | Style
Source | Contents | Index | Style
. Click on the Index link. What is St
?
St
is defined as
type St r = RWS () Text Int r
RWS
is just a monad which has Reader, Writer and State capabilities. The Reader environment is ()
, it writes Text
values and an Int
for state.
It's a convenience definition (available from the mtl
package) so you don't have to build up your own monad transformer stack using ReaderT
, WriterT
and StateT
.
Note that the reader environment is ()
which means that library is only really using the writer and state aspects of the monad.
How does box ... >= box ...
work?
The box
function has type:
box :: Selector -> (Double, Double) -> St (Var' Selection)
which means it takes a Select
and a pair of Doubles and returns a Var' Selector
in the St
monad.
The expression box "#div1" (300,300) >>= bars n 300 (Data1D [...])
is the same as:
do vs <- box "#div1" (300,300)
bars n 300 (Data1D [...]) vs
Interpretation:
box
function to create a Var' Selection
. This happens in the St
monad. bars
with the Var' Selection
returned by step 1. What's the difference between a Var' Selection
and a Selection
? Honestly I'm not sure.
The >>>
operator
>>>
is an Arrow operator. Arrows is a way of expressing computations as a pipeline of operations. The examples on this page should give you an idea of how it's used:
https://www.haskell.org/arrows/syntax.html
Note that >>>
is defined in terms of a type-class, so exactly what it does depends on what types you are using it with. In this case it is being used with Chain () Selection
values.
If you read the docs for Chain
(link) you'll see the comment:
Chain ab behaves just like (a -> b). Val Var is the starting point of chain (= constant), and Nil is the termination of chain.
So this fragment:
-- 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
can be read as:
append
with argument "svg"
What's interesting is that the func
function has type Chain ab
-- it has an unrestricted return type b
. That's why I suspect there is the explicit type signature at the end, eg :: Chain () Selection
.
as @delta already said hoogle is your friend - the imported piece here is from Control.Category
- and it denotes the Left-to-right composition of morphisms.
The category at hand in this case is the Kleisli Category associated to a monad just a sub-category of Hask - then the >>>
operator is the missing link to make functions like
f :: m a -> m b
g :: m b -> m c
work properly with each other. while
>>= :: m a -> (a -> m b) -> m b
would not be able to do that, the specialized version of (>>>)
>>> :: (m a -> m b) -> (m b -> m c) -> ma -> m c
would give you h = (f >>> g)
a correct output.