【问题标题】:Can't seem to implement Either correctly似乎无法正确实施 Either
【发布时间】:2011-02-24 10:29:27
【问题描述】:

好的,这是我当前的代码:

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,所以我想在这里问一下。有人有什么想法吗?

【问题讨论】:

    标签: haskell


    【解决方案1】:

    导致编译问题的错误是当表达式不是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 (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 () 库函数交互,因此具有纯函数类型。通常,如果您的代码没有单子元素 (IO ()),那么以纯函数式编写会更清晰。 (当开始使用 Haskell 时,这绝对是我推荐的,而不是试图立即了解 monads/monad transformers/etc。)

    当您对 monad 更加熟悉时,您可能想查看 Control.Monad.Error [2],它可以将与 Either 类似的功能包装为 monad,并封装一些细节,例如 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

    【讨论】:

    • 嗯..谢谢。显然,当我以纯函数形式定义 checkArgs 而不是内部 main 使用 'Either (IO ()) (String, String) 工作正常:P -- paste.pocoo.org/show/344101
    【解决方案2】:

    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)。现在,您不需要对 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)
    

    【讨论】:

    • 我将使用when 函数编写代码,如下所示:paste.ubuntuusers.de/399917。这种风格更易于阅读并减少了“正确行走”代码的数量。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-09-10
    • 2017-09-10
    • 1970-01-01
    • 1970-01-01
    • 2015-04-29
    • 1970-01-01
    • 2014-05-21
    相关资源
    最近更新 更多