Last active
December 14, 2015 23:09
-
-
Save fogus/5163546 to your computer and use it in GitHub Desktop.
How do I say this Haskell?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- 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 |
Unless you mean SpecificThine | Char
foo (SpecificThing one):[] = dosomething one
foo x:xs = dosomethingElse x:xs
foo _ = error
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.
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"
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)
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
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
-- 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
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
IANAHH = I Am Not A Haskell Hacker
Obviously you can wrap the output in the either monad if you want to handle errors.