在haskell中,如何将不带do符号定义的函数转为带do定义的函数?

In haskell, how to transfer the functions defined without do notation to the function defined with do?

我是 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) => a -> [(a, t)] -> [(t, t)] -> Maybe [t]

但是,下面的函数被视为不同的函数:

fg'' a tb tc = do
    let     
        b = lookup a tb
        c = lookup b tc
    Just [b,c]

它的类型是 fg'':: (Eq b, Eq a) => a -> [(a, b)] -> [(Maybe b, b)] -> Maybe [Maybe b]。 这些让我很困惑。谁能解释一下为什么?

你说的是 b = lookup a tb。这意味着 blookup a tblookup :: Eq a => a -> [(a, b)] -> Maybe b 具有相同的类型。所以b :: Maybe b。接下来,c = lookup b tc 表示 clookup b tc 具有相同的类型。因为 b :: Maybe blookup b :: [(Maybe b, c)] -> Maybe c(记住选择的类型变量是无关紧要的,只要它们是一致的)和 c :: Maybe c。由于 Haskell 中的列表是同类的,[b, c] 意味着 bc 具有相同的类型,因此 Maybe b ~ Maybe c 意味着 b ~ c。由于 fg'' returns 的值为 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 中的值转发到 returns 一个新的 monadic 值的函数中。对于 Maybe,如果 Maybe a 结果为 Nothing,此运算符将短路,因为它没有值可前馈到 (a -> Maybe b)

monad 法则确保

do x <- return y   -- note the return here
   ...

等同于

do let x = y
   ...

但是请注意,如果没有上面的return,上面代码中的两个x是不同的类型,所以它们永远不可能等价。这是因为如果y :: m a,那么第一段代码有x :: a,而第二段代码使用x :: m a,而am a总是不同的类型。

例如,

do x <- [1]
   return x

计算结果为 [1]。相反,

do let x = [1]
   return x

计算为 [[1]]

do 表示法中,<-Monad 中取出值(在本例中为 Maybe),让您在代码中操作该值如下。 但这不是它所做的唯一事情。

它还应用了相关 Monad 的 'characteristic'。 maybe的特点是,如果箭头右边的值为Nothing,它不做任何解包(因为没有什么可解包的),只是整个计算失败,所以结果整个事情也是 Nothing.

let 绑定不会做那样的事情。它只是为一些现有值分配一个新名称,因此当您执行 let a = lookup b tb 时,a 仍然是 Maybe something 类型。另一方面,当你像 a <- lookup b tb 这样解包时,a 的类型是 something.