Skip to content

Instantly share code, notes, and snippets.

@jtbandes
Last active December 30, 2015 09:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jtbandes/c8adf9ab67ea636a2592 to your computer and use it in GitHub Desktop.
Save jtbandes/c8adf9ab67ea636a2592 to your computer and use it in GitHub Desktop.
import Control.Arrow ( (&&&) )
import qualified Control.Arrow ( first, second )
import Data.Maybe ( fromMaybe )
-- this also exists in Data.Graph.Inductive.Query.Monad
(><) :: (a -> c) -> (b -> d) -> (a, b) -> (c, d)
(><) f g = Control.Arrow.first f . Control.Arrow.second g
first :: [a] -> Maybe a
first [] = Nothing
first (x:_) = Just x
rest :: [a] -> [a]
rest [] = error "scoundrel!"
rest (_:xs) = xs
data Command = Cmd deriving (Show) -- or something more useful
lookUpCommand :: String -> Maybe Command
lookUpCommand = undefined -- or something more useful
fallbackCommand = Cmd -- or something more useful
commandAndRemainingArgs :: [String] -> (Command, [String])
commandAndRemainingArgs args =
let commandName = first args in
let maybeCommandAndMaybeRest = ((>>= lookUpCommand) &&& ((rest args) <$)) commandName in
let commandAndRest = (fromMaybe fallbackCommand >< fromMaybe []) maybeCommandAndMaybeRest in
commandAndRest
@gibiansky
Copy link

import Data.Maybe ( fromMaybe, listToMaybe )

data Command = Cmd deriving (Show) -- or something more useful

lookUpCommand :: String -> Maybe Command
lookUpCommand = undefined  -- or something more useful

fallbackCommand = Cmd -- or something more useful

commandAndRemainingArgs :: [String] -> (Command, [String])
commandAndRemainingArgs strings = (command, args)
  where
    command = fromMaybe fallbackCommand (listToMaybe args >>= lookUpCommand)
    args = case strs of
            [] -> []
            _:rem -> rem

@gibiansky
Copy link

alternative using Safe:

import Data.Maybe (fromMaybe)
import Safe (tailDef, headMay)

data Command = Cmd deriving (Show) -- or something more useful

lookUpCommand :: String -> Maybe Command
lookUpCommand = undefined  -- or something more useful

fallbackCommand = Cmd -- or something more useful

commandAndRemainingArgs :: [String] -> (Command, [String])
commandAndRemainingArgs strings = (command, args)
  where
    command = fromMaybe fallbackCommand (headMay args >>= lookUpCommand)
    args = tailDef [] strs

@jtbandes
Copy link
Author

jtbandes commented Dec 2, 2015

struct Command {}
let fallbackCommand = Command()
func lookUpCommand(name: String) -> Command? { fatalError("some impl here") }


func version1(args: [String]) -> (Command, [String]) {
    let cmd: Command
    let rest: [String]

    // fairly annoying
    if let name = args.first {
        cmd = lookUpCommand(name) ?? fallbackCommand
        rest = Array(args.suffixFrom(1))
    }
    else {
        cmd = fallbackCommand
        rest = []
    }

    return (cmd, rest)
}

// or...

func version2(args: [String]) -> (Command, [String]) {
    let cmd = args.first.flatMap(lookUpCommand) ?? fallbackCommand
    let rest = args.isEmpty ? [] : Array(args.suffixFrom(1))  // mildly annoying
    return (cmd, rest)
}

@jtbandes
Copy link
Author

jtbandes commented Dec 2, 2015

While my original using >>= / &&& / <$ / >< is horrible, I like that it makes a very clear pipeline:

                             lookUpCommand     fallbackCommand
               first            Command?   -->     Command
input [String]  -->  String? < 
                                [String]?  -->     [String]
                                  rest          fallback to []

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment