Skip to content

Instantly share code, notes, and snippets.

@fogus
Last active December 14, 2015 23:09
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save fogus/5163546 to your computer and use it in GitHub Desktop.
Save fogus/5163546 to your computer and use it in GitHub Desktop.
How do I say this Haskell?
-- THE SEARCH CONTINUES
data GenericThing = SpecificThing Char
| DifferentSpecificThing Double
| AnotherSpecificThing String
| YetAnotherDifferentSpecificThing Integer
foo :: [GenericThing] -> GenericThing
foo [] = return $ AnotherSpecificThing ""
foo [SpecificThing one] = -- do something
foo (SpecificThing c : cs) = -- do something else
foo badThings = -- throw if given any other kind of Thing
-- How can I express
-- - Do something for an array of one SpecificThing
-- - Do something else for an array of many SpecificSomethings
-- - Otherwise throw an error
-- The code above only checks the head of the lists, it does not type check the rest in the many case
--- My answer
data GenericThing = SpecificThing Char
| DifferentSpecificThing Double
| AnotherSpecificThing String
| YetAnotherDifferentSpecificThing Integer
extricate :: ThrowsError Char
extricate (SpecificThing c) = return c
extricate notChar = throwError $ FrobnicateError notChar
foo :: [GenericThing] -> GenericThing
foo [] = return $ AnotherSpecificThing ""
foo [SpecificThing one] = -- do something
foo things = mapM extricate things >>= return . AnotherSpecificThing
@johnbender
Copy link

IANAHH = I Am Not A Haskell Hacker

foo x:[] = dosomething x
foo x:xs = dosomethingElse x:xs
foo _ = error

Obviously you can wrap the output in the either monad if you want to handle errors.

@johnbender
Copy link

Unless you mean SpecificThine | Char

foo (SpecificThing one):[] = dosomething one
foo x:xs = dosomethingElse x:xs
foo _ = error

@hellige
Copy link

hellige commented Mar 14, 2013

If I didn't really want to decompose the multi-element case into x:xs, I would do:

foo [] = undefined
foo [x] = dosomething x
foo xs = dosomethingElse xs

Remember: order of cases matters.

@jvranish
Copy link

 foo :: [GenericThing] -> GenericThing
 foo [SpecificThing one] = -- do something
 foo [SpecificThing a, SpecificThing b] = -- do something for a list with two specific things in it

 foo (thing:things) = -- do something with _at least_ one thing
 -- note in this example the following pattern will never get hit because the above pattern would have already matched anything that would fit here
 foo (SpecificThing a:SpecificThing b:_) = -- do something for a list with _at least_ two specific things in it
 foo badThings = error "Given our patterns above, this could only happen if the list is empty"

@fogus
Copy link
Author

fogus commented Mar 14, 2013

At the moment I have foo things = -- do something else, but that seems janky and fails with an inexhauted pattern thingy (I'm being less than technical here)

@dyokomizo
Copy link

foo :: [GenericThing] -> GenericThing
foo  = bar $ map $ \st@(SpecificThing _) -> st -- will call error if there's a bad match
where bar [SpecificThing one] = -- do something
      bar many = -- many specific things

@jvranish
Copy link

Perhaps something like this?:

foo :: [GenericThing] -> GenericThing
foo xs = SpecificThing $ doThings [c | SpecificThing c <- xs]

doThings :: [Char] -> Char
doThings (x:xs) = -- do something
doThings [] = -- we didn't get any SpecificThings, error

@adamsmasher
Copy link

-- there's probably a GHC extension to autogenerate this
isSpecificThing :: GenericThing -> Bool
isSpecificThing (SpecificThing _) = True
isSpecificThing _ = False

-- this is the easy case, of course
foo [SpecificThing one] = doSomething one

-- Haskell doesn't know at compile time whether an array contains only SpecificThings
-- so we need to dynamically test for it
foo xs | all xs isSpecificThing = doSomethingElse xs
       | otherwise              = error "Whatever"

-- Although "Good Haskell" doesn't really use error often.
-- Instead you'd use a Maybe or Either probably.

-- Of course, this is fundamentally kind of a confused question.
-- "Good Haskell" would have you design your types/structure in such a way
-- that you don't need to dynamically test if each thing is a SpecificThing
-- probably by making a GenericInterface typeclass
-- and then making each SpecificTypeOfThing its own type
-- rather than have them share a sum type

@dysinger
Copy link

Q:

-- How can I express
-- - Do something for an array of one SpecificThing
-- - Do something else for an array of many SpecificSomethings
-- - Otherwise throw an error

A:

data GenericThing = SpecificThing Char
                  | DifferentSpecificThing Double
                  | YetAnotherDifferentSpecificThing Integer
                  deriving Show

-- showing the use of exceptions (error)

foo :: [GenericThing] -> GenericThing
foo a@(x:xs)
  | specific x && null xs = oneSpecificThing x
  | all specific a        = lotsOfSpecificThings a
  | otherwise             = error "Not All SpecificThings"
  where specific (SpecificThing _) = True
        specific _                 = False
        oneSpecificThing           = id   -- or other impl
        lotsOfSpecificThings       = head -- or other impl
foo _ = error "Bad Input!?"

-- showing another way to do it by using Either

bar :: [GenericThing] -> Either String GenericThing
bar a@(x:xs)
  | specific x && null xs = Right $ oneSpecificThing x
  | all specific a        = Right $ lotsOfSpecificThings a
  | otherwise             = Left "Not All SpecificThings"
  where specific (SpecificThing _) = True
        specific _                 = False
        oneSpecificThing           = id   -- or other impl
        lotsOfSpecificThings       = head -- or other impl
bar _ = Left "Bad Input!?"

-- showing another way to do it with Maybe

baz :: [GenericThing] -> Maybe GenericThing
baz a@(x:xs)
  | specific x && null xs = Just $ oneSpecificThing x
  | all specific a        = Just $ lotsOfSpecificThings a
  | otherwise             = Nothing
  where specific (SpecificThing _) = True
        specific _                 = False
        oneSpecificThing           = id   -- or other impl
        lotsOfSpecificThings       = head -- or other impl
baz _ = Nothing

-- main so you can try it - use `runhaskell ./Gist.hs`

main :: IO ()
main = do
  putStrLn . show $ foo [SpecificThing 'u']
  putStrLn . show $ bar [SpecificThing 'a', SpecificThing 'b']
  putStrLn . show $ baz [SpecificThing 'a', DifferentSpecificThing 2.0]
  putStrLn . show $ bar [YetAnotherDifferentSpecificThing 5]
  putStrLn . show $ foo [YetAnotherDifferentSpecificThing 5, DifferentSpecificThing 2.0]

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