我是Haskell的新手。现在我有关于类型的一些问题。 据说功能:
fg a tb tc = case lookup a tb of
Nothing -> Nothing
Just b -> case lookup b tc of
Nothing -> Nothing
Just c -> Just [b,c]
与函数相同:
fg' a tb tc = do
b <- lookup a tb
c <- lookup b tc
Just [b,c]
他们的类型都是fg ::(Eq t,Eq a)=&gt; a - &gt; [(a,t)] - &gt; [(t,t)] - &gt;也许[t]
但是,以下功能被视为不同的功能:
fg'' a tb tc = do
let
b = lookup a tb
c = lookup b tc
Just [b,c]
它的类型是fg''::(Eq b,Eq a)=&gt; a - &gt; [(a,b)] - &gt; [(可能b,b)] - &gt;也许[也许b]。 这些让我困惑。有人能解释原因吗?
答案 0 :(得分:3)
你说b = lookup a tb
。这意味着b
与lookup a tb
和lookup :: Eq a => a -> [(a, b)] -> Maybe b
具有相同的类型。所以b :: Maybe b
。接下来,c = lookup b tc
表示c
与lookup b tc
的类型相同。自b :: Maybe b
,lookup b :: [(Maybe b, c)] -> Maybe c
以来(请记住所选的类型变量无关紧要,只要它们一致),c :: Maybe c
。由于Haskell中的列表是同构的,[b, c]
表示b
和c
具有相同的类型,因此Maybe b ~ Maybe c
隐含b ~ c
。由于fg''
返回的值为Just [b, c]
,这意味着Just [b, c] :: Maybe [Maybe b]
。
Haskell中的<-
语法与=
中的let
不等效。在您的情况下,fg'
可以使用do
运算符转换为不>>=
的函数,Maybe
的运算符类型为
(>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
可以用作
fg''' a tb tc =
lookup a tb >>= (\b -> -- b <- lookup a tb
lookup b tc >>= (\c -> -- c <- lookup b tc
Just [b, c] -- Just [b, c]
)
)
将>>=
视为将包含在monad中的值转换为返回新monadic值的函数。对于Maybe
,如果Maybe a
原来是Nothing
,则此运算符会短路,因为它不会有值向前馈入(a -> Maybe b)
。
答案 1 :(得分:3)
monad法律确保
do x <- return y -- note the return here
...
相当于
do let x = y
...
但请注意,如果没有上面的return
,上面代码中的两个x
具有不同的类型,因此它们永远不会是等价的。这是因为如果y :: m a
,则第一段代码为x :: a
,而第二段使用x :: m a
,a
和m a
始终为不同类型。
例如,
do x <- [1]
return x
评估为[1]
。取而代之的是,
do let x = [1]
return x
评估为[[1]]
。
答案 2 :(得分:1)
在do
表示法中,<-
取值Monad
(在这种情况下为Maybe
)并让您操纵该值随后的代码。
但这不是唯一的事情。
它也适用于&#39;特征&#39;有问题的Monad
。可能的特征是,如果箭头右侧的值为Nothing
,则它不会进行任何展开(因为没有任何东西要打开)并且只是无法进行整个计算,所以整件事的结果也是Nothing
。
let
绑定不会做那样的事情。它只是为某个现有值指定了新名称,因此当您执行let a = lookup b tb
时,a
仍然属于Maybe something
类型。另一方面,当您像a <- lookup b tb
一样打开它时,a
的类型为something
。