所以我正在玩GHCi中的hasbolt模块,我对一些贬低有好奇心。我通过创建管道连接到Neo4j数据库,如下所示
ghci> pipe <- connect $ def {credentials}
这很好用。但是,我想知道(<-)
运算符的类型是什么(GHCi不会告诉我)。最令人沮丧的解释描述了
do x <- a
return x
desugars to
a >>= (\x -> return x)
但是x <- a
行呢?
它并不能帮助我添加return
,因为我希望pipe :: Pipe
不是pipe :: Control.Monad.IO.Class.MonadIO m => m Pipe
,而是(>>=) :: Monad m => m a -> (a -> m b) -> m b
所以尝试使用bind
和{{}}进行desugar {1}} / return
没有它就无法运作。
理想情况下,最好只设置pure
个实例以启用Comonad
作为extract :: Monad m => m a -> a
,但它让我觉得我不明白pipe = extract $ connect $ def {creds}
。
另一个奇怪的是,将(<-)
视为haskell函数,它的第一个参数是一个超出范围的变量,但这并不意味着
(<-)
因为不只是任何东西都可以用作自由变量。例如,您无法将管道绑定到(<-) :: a -> m b -> b
类型或Num
。变量必须是&#34; String&#34; ish的东西,除了它实际上从来不是Bool
;并且你绝对不能尝试绑定到String
。因此,似乎它不是通常意义上的haskell函数(除非有一类函数从自由变量名称空间获取值...不太可能)。那么String
究竟是什么?可以使用(<-)
完全替换它吗?这是desugar /绕过它的最好方法吗?
答案 0 :(得分:8)
我想知道(&lt ;-)运算符的类型是什么......
<-
没有类型,它是do
符号语法的一部分,如您所知,它被转换为>>=
和{的序列在一个名为desugaring的过程中{1}}。
但只是x&lt; - a ......?
这一行呢?
这是正常haskell代码中的语法错误,编译器会抱怨。行原因:
return
在ghci中工作的是repl是一种ghci> pipe <- connect $ def {credentials}
块;您可以将每个条目视为do
函数中的一行(它比那更毛茸茸,但这是一个很好的近似值)。这就是为什么你需要(直到最近)在ghci中说main
来声明绑定的原因。
答案 1 :(得分:4)
理想情况下,最好只使一个Comonad实例启用使用extract :: Monad m =&gt; m a - &gt; a as pipe = extract $ connect $ def {creds}但它让我感到烦恼,我不理解(&lt; - )。
Comonad与Monads无关。事实上,大多数Monads都没有任何有效的Comonad实例。考虑[]
Monad:
instance Monad [a] where
return x = [x]
xs >>= f = concat (map f xs)
如果我们尝试编写Comonad实例,我们无法定义extract :: m a -> a
instance Comonad [a] where
extract (x:_) = x
extract [] = ???
这告诉我们关于Monads的一些有趣的事情,即我们不能写出类型为Monad m => m a -> a
的通用函数。换句话说,我们无法提取&#34;来自Monad的价值而没有关于它的额外知识。
那么do-notation语法do {x <- [1,2,3]; return [x,x]}
如何工作?
由于<-
实际上只是语法糖,就像[1,2,3]
实际上意味着1 : 2 : 3 : []
一样,上面的表达式实际上意味着[1,2,3] >>= (\x -> return [x,x])
,而concat (map (\x -> [[x,x]]) [1,2,3]))
又评估为{{1} } {,来自[1,1,2,2,3,3]
。
注意箭头如何转换为>>=
和lambda。这仅使用内置(在类型类中)Monad函数,因此它适用于任何Monad。
我们可以假装使用(>>=) :: Monad m => m a -> (a -> m b) -> m b
并使用&#34;提取的&#34;来提取值。我们提供的函数内部a
,就像上面列表示例中的lambda一样。但是,实际上不可能以通用方式从Monad中获取值,这就是>>=
的返回类型为m b
(在Monad中)的原因
答案 2 :(得分:2)
那么
(<-)
究竟是什么?可以使用extract
完全替换它吗?这是desugar /绕过它的最好方法吗?
请注意,即使对于同时包含<-
和extract
个实例的类型,do-block Monad
和Comonad
也意味着非常不同。例如,考虑non-empty lists。他们有Monad
的实例(非常类似于列表的常用)和Comonad
(extend
/ =>>
将一个函数应用于列表的所有后缀)。如果我们写一个do-block,比如......
import qualified Data.List.NonEmpty as N
import Data.List.NonEmpty (NonEmpty(..))
import Data.Function ((&))
alternating :: NonEmpty Integer
alternating = do
x <- N.fromList [1..6]
-x :| [x]
... x
中的x <- N.fromList [1..6]
代表非空列表的元素;但是,此x
必须用于构建新列表(或更一般地,用于设置新的monadic计算)。正如其他人所解释的那样,这反映了如何使用符号。我们可以更容易地看到我们是否将脱落的代码看作原始代码:
alternating :: NonEmpty Integer
alternating =
N.fromList [1..6] >>= \x ->
-x :| [x]
GHCi> alternating
-1 :| [1,-2,2,-3,3,-4,4,-5,5,-6,6]
do-block中x <- N.fromList [1..6]
以下的行相当于lambda的主体。因此,x <-
孤立地类似于没有身体的lambda,这不是一件有意义的事情。
需要注意的另一个重要事项是,上面的do-block中的x
与任何一个Integer
都不对应,而是与所有 Integer
相对应在列表中。这已经说明<-
不对应于提取函数。 (对于其他monad,x
甚至可能根本不对应任何值,例如x <- Nothing
或x <- []
。另请参阅Lazersmoke's answer。)
另一方面,extract
确实提取了一个值,没有ifs或buts ......
GHCi> extract (N.fromList [1..6])
1
...但是,它实际上是单个值:列表的尾部被丢弃。如果我们想使用列表的后缀,我们需要extend
/ (=>>)
...
GHCi> N.fromList [1..6] =>> product =>> sum
1956 :| [1236,516,156,36,6]
如果我们有comonads的共同注释(参见this package及其中的链接),上面的示例可能会被重写为以下内容:
-- codo introduces a function: x & f = f x
N.fromList [1..6] & codo xs -> do
ys <- product xs
sum ys
这些陈述与普通价值相对应;绑定变量(xs
和ys
),对于comonadic值(在本例中为列出后缀)。这与我们对monadic do-blocks的结果正好相反。总而言之,就您的问题而言,切换到comonads只是交换我们无法在计算环境之外引用的内容。