为什么Haskell中的gcd函数仅在返回Writer [String] Int类型而不返回Writer String Int类型时起作用?

时间:2019-10-01 13:25:57

标签: haskell monads

以下代码不起作用:

gcd' :: Int -> Int -> Writer String Int
gcd' a b 
 | b == 0 = do
     tell "gcd " ++ show a ++ " " ++ show b ++ " \n"
     return a
 | otherwise = do
     tell "gcd " ++ show a ++ " " ++ show b ++ " \n"
     gcd' b (a `mod` b)

当我将代码更改为此时,它现在可以工作:

gcd' :: Int -> Int -> Writer [String] Int
gcd' a b 
 | b == 0 = do
     tell ["gcd " ++ show a ++ " " ++ show b ++ " \n"]
     return a
 | otherwise = do
     tell ["gcd " ++ show a ++ " " ++ show b ++ " \n"]
     gcd' b (a `mod` b)

尽管我现在必须串联字符串列表。

我不明白为什么我的原始代码不起作用。当然,我的原始代码应将每个步骤中的字符串连接起来,以提供总体日志。但是,相反,它给了我以下错误消息:

gcdLogger.hs:6:6: error:
    • Couldn't match type ‘[]’
                     with ‘WriterT String Data.Functor.Identity.Identity’
      Expected type: WriterT String Data.Functor.Identity.Identity ()
        Actual type: [()]
    • In a stmt of a 'do' block:
        tell "gcd " ++ show a ++ " " ++ show b ++ " /n"
      In the expression:
        do tell "gcd " ++ show a ++ " " ++ show b ++ " /n"
           return a
      In an equation for ‘gcd'’:
          gcd' a b
            | b == 0
            = do tell "gcd " ++ show a ++ " " ++ show b ++ " /n"
                 return a
            | otherwise
            = do tell "gcd " ++ show a ++ " " ++ show b ++ " /n"
                 gcd' b (a `mod` b)

1 个答案:

答案 0 :(得分:3)

您的第一次尝试不起作用,因为函数应用程序优先于运算符。因此它被解析为:

(tell "gcd ") ++ (show a) ++ " " ++ (show b) ++ "\n "

在第二次尝试中,您每次都将字符串包装在一个单例列表中,然后编写一个String的列表(所有这些列表都包含一个字符串,但是仍然不能使这些{{ 1}} s。

您可以在此处添加括号来解决该问题:

String

例如:

gcd' :: Int -> Int -> Writer String Int
gcd' a b = do
    tell ("gcd " ++ show a ++ " " ++ show b ++ " \n")
    if b == 0 then
        return a
    else
        gcd' b (a `mod` b)

请注意,出于调试目的,您可以更方便地使用trace :: String -> a -> a而不是Prelude Control.Monad.Trans.Writer.CPS> runWriter (gcd' 15 5) (5,"gcd 15 5 \ngcd 5 0 \n") ,因为它可以更好地完成您想要的操作。