2011-02-24 89 views
1

好吧所以这里是我当前的代码:似乎无法实现正确要么

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 ([email protected](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 

的主要问题是与检查。我试图实现任何一种类型,因为我希望它不是出错,或其他功能使用返回的东西,但是,它目前示数出有:

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和避风港开始了”对于monads来说,处理得太多了,所以我想我会在这里问。有人有主意吗?

回答

3

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

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

我做你的ARG检查功能的快速改写为

checkArgs :: [String] -> Either String (String, [String]) 
checkArgs args = 
    case args of 
     [] -> Left "No arguments given. See --help for more info." 
     _ -> let ([email protected](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()库函数交互等方面具有纯功能型。一般来说,如果您的代码没有一元元素(IO()),则可以更纯粹地将其编写为纯功能样式。 (当在Haskell中开始时,这绝对是我会推荐的,而不是试图让你的头立即在monads/monad变形金刚/等等。)

当你对单子更舒适时,你可能想要退房Control.Monad.Error [2],其可以将与Either类似的功能作为单元包装,并且将封装一些细节,如始终为计算错误的Left

[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

+0

HM ..谢谢。显然,当我在纯函数形式中定义checkArgs而不是在main中使用'Either(IO())(String,String)工作正常:P - http://paste.pocoo.org/show/344101/ – 2011-02-24 22:16:42

1

Either (IO()) (String, [String])是包含一个IO动作或 (String, [String]),所以这种类型的值可以是Left IO()Right (String, [String])一个类型。 Left值通常表示Haskell中发生的错误 。这个错误可以用你想要的任何类型表示,例如 ,错误代码(Int)或者说String表示发生了什么。 如果您使用IO()作为表示错误的类型,那么您将无法通过 来提取有关该错误的任何信息。您稍后可以执行IO操作。

您要找的型号不是Either (IO()) (String, [String]), 是Either String (String, [String])。使用此类型可以获取有关 错误(String)的信息。现在,你不需要任何IO操作成Either类型,所以你 可以删除所有do表达式:

let check = case args of 
       [] -> Left "No arguments given. See --help for more info."          
       _ -> let ([email protected](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) 
+2

我会使用'when'函数编写代码如下:http://paste.ubuntuusers.de/399917/。这种风格更容易阅读,并减少了“正确行走”代码的数量。 – fuz 2011-02-24 18:37:47