我目前正在尝试学习 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/