Skip to content

Instantly share code, notes, and snippets.

@ObjectBoxPC
Last active July 7, 2020 13:48
Show Gist options
  • Save ObjectBoxPC/7dbdfedc2482d364a41445e5322e54ff to your computer and use it in GitHub Desktop.
Save ObjectBoxPC/7dbdfedc2482d364a41445e5322e54ff to your computer and use it in GitHub Desktop.
Simple Haskell program to check if the input (command-line arguments) form a pangram, containing all 26 letters of the English alphabet
import Data.Char (toUpper)
import Data.List (intercalate)
import Data.List.NonEmpty (NonEmpty, nonEmpty, toList)
import System.Environment (getArgs)
data PangramResult = Pangram | MissingLetters (NonEmpty Char)
checkPangram :: String -> PangramResult
checkPangram s = case nonEmpty missingLetters of
Just ls -> MissingLetters ls
Nothing -> Pangram
where
missingLetters = filter (not . (`elem` normalized)) ['A'..'Z']
normalized = map toUpper s
getText :: IO String
getText = concat <$> getArgs
showLetters :: NonEmpty Char -> String
showLetters = intercalate ", " . map (:[]) . toList
main = do
text <- getText
let
message = case checkPangram text of
Pangram -> "Pangram"
MissingLetters ls -> "Not a pangram: Missing " ++ showLetters ls
putStrLn message
@ObjectBoxPC
Copy link
Author

@friedbrice Thanks a lot for your detailed comments. I've made changes based on your suggestions.

Some specific responses:

  • I originally had something very similar to your alternative implementation of allElems/subset, but I suppose I went a bit overboard in trying to make it point-free. I wasn't used to using functions as infix operators so I had (flip elem) instead of (`elem` xs) However, thinking of elem and subset as set symbols does make the infix version more intuitive to me.
  • My implementation of main was meant to convey a sort of pipeline like in a Unix shell. But do-notation version does make it shorter so I went for that.

@friedbrice
Copy link

friedbrice commented Jul 6, 2020

@ObjectBoxPC I'm glad it was helpful. I definitely relate to the tendency to want to write your main as a pipeline, so I tend to favor the putStrLn . msg . concat =<< getArgs style. It never bothered me that the entrance to the pipeline is on the right side, because that's how function application is written in every programming language and traditionally on paper.

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