似乎无法正确实现

时间:2011-02-24 10:29:27

标签: haskell

好吧,这是我现在的代码:

import System.IO
import System.Environment
import System.Directory

main = do
  unfiltered <- getArgs ; home <- getHomeDirectory ; let db = home ++ "/.grindstone"

  case unfiltered of
    (x:xs) -> return ()
    _      -> error "No command given. See --help for more info."
  command:args <- getArgs

  createDirectoryIfMissing True db

  let check = case args of
              [] -> error "No arguments given. See --help for more info."
              _  -> do let (params@(param:_),rest) = span (\(c:_) -> c=='-') args
                       if length params > 1 then error ("No arguments given for " ++ param)
                         else do
                       let (pArgs,_) = span (\(c:_) -> c/='-') rest
                       return (param, pArgs) :: Either (IO ()) (String, [String])

  let add = print "sup"

  let cmds = [("add", add)]
  let action = lookup command cmds

  case action of
    Nothing -> error "Unknown command."
    (Just action) -> action

主要问题是检查。我尝试实现Either类型,因为我希望它出错,或返回一些东西供另一个函数使用,但是,它当前错误地用:

grindstone.hs:21:23:
    No instance for (Monad (Either (IO ())))
      arising from a use of `return' at grindstone.hs:21:23-43
    Possible fix:
      add an instance declaration for (Monad (Either (IO ())))
    In the expression:
          return (param, pArgs) :: Either (IO ()) (String, [String])
    In the expression:
        do { let (pArgs, _) = span (\ (c : _) -> ...) rest;
               return (param, pArgs) :: Either (IO ()) (String, [String]) }
    In the expression:
        if length params > 1 then
            error ("No arguments given for " ++ param)
        else
            do { let (pArgs, _) = ...;
                   return (param, pArgs) :: Either (IO ()) (String, [String]) }

我刚刚开始使用haskell而且还没有对monad进行太多处理,所以我想在这里问一下。有人有什么想法吗?

2 个答案:

答案 0 :(得分:3)

导致编译问题的错误是,当表达式不是Either (IO ()) (String, [String])值时,您直接将表达式转换为类型Either。 (编译器没有输出非常有用的错误消息。)

要创建Either值[1],我们使用数据构造函数LeftRight。约定(来自库页面)是错误是Left值,而正确的值是Right值。

我快速重写你的arg检查功能

checkArgs :: [String] -> Either String (String, [String])
checkArgs args = 
    case args of
      [] -> Left "No arguments given. See --help for more info."
      _  -> let (params@(param:_),rest) = span (\(c:_) -> c=='-') args in
            if length params > 1 then 
               Left ("No arguments given for " ++ param)
            else 
               let (pArgs,_) = span (\(c:_) -> c/='-') rest in
               Right (param, pArgs)

请注意,arg检查功能不与任何外部IO ()库函数交互,因此具有纯函数类型。一般来说,如果你的代码没有monadic元素(IO ()),那么用纯函数风格编写代码会更清楚。 (当我在Haskell开始时,这绝对是我推荐的东西,而不是试图立即绕过monads / monad变换器/等。)

当你对monad更加熟悉时,你可能想要查看Control.Monad.Error [2],它可以包含与Either类似的功能作为monad,并封装一些细节,如{{ 1}}总是计算错误。

[1] http://www.haskell.org/ghc/docs/6.12.2/html/libraries/base-4.2.0.1/Data-Either.html

[2] http://hackage.haskell.org/packages/archive/mtl/1.1.0.2/doc/html/Control-Monad-Error.html

答案 1 :(得分:1)

Either (IO ()) (String, [String])是包含IO操作或类型的类型 (String, [String]),因此此类型的值可以是Left IO ()Right (String, [String])Left值通常表示错误 在哈斯克尔发生。此错误可以用您想要的任何类型表示, 例如,错误代码(Int)或String说明发生了什么。 如果您使用IO ()作为表示错误的类型,您将无法使用 提取有关错误的任何信息。您稍后将能够执行IO操作。

您要查找的类型不是Either (IO ()) (String, [String]), 是Either String (String, [String])。有了这种类型就可以获得有关的信息 错误(String)。现在,您不需要Either类型的任何IO操作,所以你 可以删除所有do表达式:

let check = case args of
                [] -> Left "No arguments given. See --help for more info."                                    
                _  -> let (params@(param:_),rest) = span (\(c:_) -> c=='-') args
                      in if length params > 1
                         then Left ("No arguments given for " ++ param)
                         else let (pArgs,_) = span (\(c:_) -> c/='-') rest    
                              in Right (param, pArgs)