So I'm playing around with the hasbolt module in GHCi and I had a curiosity about some desugaring. I've been connecting to a Neo4j database by creating a pipe as follows
ghci> pipe <- connect $ def {credentials}
and that works just fine. However, I'm wondering what the type of the (<-)
operator is (GHCi won't tell me). Most desugaring explanations describe that
do x <- a
return x
desugars to
a >>= (\x -> return x)
but what about just the line x <- a
?
It doesn't help me to add in the return
because I want pipe :: Pipe
not pipe :: Control.Monad.IO.Class.MonadIO m => m Pipe
, but (>>=) :: Monad m => m a -> (a -> m b) -> m b
so trying to desugar using bind
and return
/pure
doesn't work without it.
Ideally it seems like it'd be best to just make a Comonad
instance to enable using extract :: Monad m => m a -> a
as pipe = extract $ connect $ def {creds}
but it bugs me that I don't understand (<-)
.
Another oddity is that, treating (<-)
as haskell function, it's first argument is an out-of-scope variable, but that wouldn't mean that
(<-) :: a -> m b -> b
because not just anything can be used as a free variable. For instance, you couldn't bind the pipe to a Num
type or a Bool
. The variable has to be a "String"ish thing, except it never is actually a String
; and you definitely can't try actually binding to a String
. So it seems as if it isn't a haskell function in the usual sense (unless there is a class of functions that take values from the free variable namespace... unlikely). So what is (<-)
exactly? Can it be replaced entirely by using extract
? Is that the best way to desugar/circumvent it?
I'm wondering what the type of the (<-) operator is ...
<-
doesn't have a type, it's part of the syntax of do
notation, which as you know is converted to sequences of >>=
and return
during a process called desugaring.
but what about just the line x <- a ...?
That's a syntax error in normal haskell code and the compiler would complain. The reason the line:
ghci> pipe <- connect $ def {credentials}
works in ghci is that the repl is a sort of do
block; you can think of each entry as a line in your main
function (it's a bit more hairy than that, but that's a good approximation). That's why you need (until recently) to say let foo = bar
in ghci to declare a binding as well.