开发者

Can't seem to implement Either correctly

开发者 https://www.devze.com 2023-02-12 22:49 出处:网络
Alright so here\'s my current code: import System.IO import System.Environment import System.Directory main = do

Alright so here's my current code:

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

The main problem is with check. I tried implementing the Either type since I want it to either error out, or return something for another function to use, but, it's currently erroring out with:

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]) }

I'm only starting out in haskell and haven't dealt too much with monads yet so just thought I'd ask on here. anyone have any ideas?


The error that is causing your compile problems is that you are directly casting an expression to the type Either (IO ()) (String, [String]) when it is not an Either value. (The compiler is not outputting a very helpful error message.)

To create an Either value [1], we use the data constructors Left and Right. Convention (from the library page) is that errors are a Left value, while correct values are a Right value.

I did a quick rewrite of your arg checking function as

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)

Note that the arg checking function does not interact with any external IO () library functions and so has a purely functional type. In general if your code does not have monadic elements (IO ()), it can be clearer to write it in purely functional style. (When starting out in Haskell this is definitely something I would recommend rather than trying to get your head around monads/monad transformers/etc immediately.)

When you are a little more comfortable with monads, you may want to check out Control.Monad.Error [2], which can wraps similar functionality as Either as a monad and would encapsulate some details like Left always being computation errors.

[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


Either (IO ()) (String, [String]) is a type that contains an IO action or a (String, [String]), so values of this type could be Left IO () or Right (String, [String]). Left values usually represents an error occurrence in Haskell. This error can be represented with any type you want, for example, an error code (Int) or a String that says what happened. If you use IO () as the type which represents an error, you won't be able to extract any information about the error. You just will able to perform an IO action later on.

The type that you are looking for isn't Either (IO ()) (String, [String]), is Either String (String, [String]). With this type can get information about the error (String). Now, you dont need any IO action into Either type, so you can remove all do expressions:

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)
0

精彩评论

暂无评论...
验证码 换一张
取 消