haskell - 将两个 "Maybe"单子(monad)中的值相乘?

标签 haskell maybe

我目前正在尝试学习 Haskell,遇到了一个关于 Maybe 的奇怪问题。我似乎无法弄清楚的单子(monad)。

作为一个实验,我目前正在尝试获取一个字符串,将每个字母转换为任意数字,然后将它们相乘/组合在一起。这是我到目前为止所拥有的:

lookupTable :: [(Char, Int)]
lookupTable = [('A', 1), ('B', 4), ('C', -6)]

strToInts :: String -> [Maybe Int]
strToInts = map lookupChar
    where 
        lookupChar :: Char -> Maybe Int
        lookupChar c = lookup c lookupTable

-- Currently fails
test :: (Num n, Ord n) => [Maybe n] -> [Maybe n]
test seq = [ x * y | (x, y) <- zip seq $ tail seq, x < y ]

main :: IO ()
main = do
    putStrLn $ show $ test $ strToInts "ABC"

当我尝试运行它时,它返回以下错误:
test.hs:13:16:
    Could not deduce (Num (Maybe n)) arising from a use of `*'
    from the context (Num n, Ord n)
      bound by the type signature for
                 test :: (Num n, Ord n) => [Maybe n] -> [Maybe n]
      at test.hs:12:9-48
    Possible fix: add an instance declaration for (Num (Maybe n))
    In the expression: x * y
    In the expression: [x * y | (x, y) <- zip seq $ tail seq]
    In an equation for `test':
        test seq = [x * y | (x, y) <- zip seq $ tail seq]

我不是 100% 确定为什么会发生这个错误,或者它到底意味着什么,尽管我怀疑这可能是因为我试图将两个 Maybe 相乘monads -- 如果我改变 test 的定义以下,程序编译并运行良好:
test :: (Num n, Ord n) => [Maybe n] -> [Maybe n]
test seq = [ x | (x, y) <- zip seq $ tail seq, x < y ]

我也尝试将类型声明更改为以下内容,但这也不起作用。
test :: (Num n, Ord n) => [Maybe n] -> [Num (Maybe n)]

我不确定如何解决此错误。我对 Haskell 还很陌生,所以我可能只是缺少一些非常简单的东西,或者我的结构完全错误,但这让我很困惑。我究竟做错了什么?

最佳答案

Maybe 没有 num 实例,因此您不能将它们直接相乘。您需要以某种方式将纯函数应用于上下文中的值。这正是应用仿函数的用途!

应用仿函数存在于 Control.Applicative 中:

import Control.Applicative

所以你有这个函数,你想将它应用于上下文中的 2 个参数:
(*) :: Num a => a -> a -> a

您可能了解了 fmap,它接受一个函数并将其应用于上下文中的一个值。 <$>是 fmap 的别名。当我们将纯函数映射到可能的值上时,我们得到以下结果:
(*) <$> Just 5 :: Num a => Maybe (a -> a)

所以现在我们可能有一个函数,我们需要将它应用到一个值上,这正是 applicative functor 所做的。它的主要运营商是<*>其中有签名:
(<*>) :: f (a -> b) -> f a -> f b

当我们专门化它时,我们得到了我们需要的功能:
(<*>) :: Maybe (a -> b) -> Maybe a -> Maybe b

我们应用它,输出是您期望的数字。
(*) <$> Just 5 <*> Just 5 :: Num a => Maybe a

因此,要编译您的代码,您需要更改您的测试函数以使用 <$><*> ,看看你能不能弄清楚如何。

关于haskell - 将两个 "Maybe"单子(monad)中的值相乘?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22268226/

相关文章:

return-value - 如何在 Haskell 中获取 Maybe 的值

haskell - 如何使用秒差距进行 sepBy 模糊解析?

performance - Haskell:对常量表达式进行不必要的重新计算

haskell - 在Network.URI上无法匹配Maybe vs Maybe类型

haskell - Haskell 中的 EAFP

haskell - 也许合并运算符

haskell - 双仿函数与箭头方法

list - 比计算单位列表长度更好的方法

haskell - 如何映射和连接文件路径?