Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Looking For A Standard Haskell Idiom Before I Implement it Myself

Tags:

haskell

I have defined a datatype RuleFile and I writing a function that examines a Rulefile with a number of predicates, accumulating warning strings as it goes along.

rfWarnings :: RuleFile -> [String]
rfWarnings rf = let warnings = [((\rf -> length (neigh rf) > 3), "warning: unknown extra information in the neighborhood, discarding it and moving on"),
                               <more warnings> ]
                in <process> warnings

Is there some off-the-shelf haskell function for <process>? Or maybe I'm not thinking about this in an idiomatic haskell way, and there's some other recommended approach? I'm using Control.Monad.Except for errors, but my current thinking is I want to treat warnings separately and output the messages on stderr.

Later on, I'll use it with something like ...

main :: IO ()
main = do
 <some stuff>
 ruleFiles <- <produce them>
 mapM (hPutStr stderr) (map rfWarnings ruleFiles)
 <other stuff>
like image 962
seewalker Avatar asked Dec 03 '25 09:12

seewalker


2 Answers

You could use a Writer for this:

import Control.Monad.Writer

rfWarnings rf = execWriter $ do
    unless (length (neigh rf) <= 3) $ tell "Too much neigh!"
    unless (isFrobulastic rf) $ tell "Frobulation mismatch!"

This has the advantage that it is very easy to compose rfWarnings from smaller pieces, e.g.

rfWarnings rf = execWriter $ do
  unless (looksOK rf) $ tell "Completely b0rken!"
  mapM_ partWarning (parts rf)

and also, I'd rather use unless than when because I think describing the correct state is better than describing the incorrect one.

like image 168
Cactus Avatar answered Dec 05 '25 22:12

Cactus


Not sure if this works for your application – but, an idiomatic thing might be to change the type of your predicates: rather than a fixed string and a function that returns Bool (but in the Nothing case you don't actually need the string), use only a function – which returns Maybe String. Then what you're asking about becomes very simple: apply all functions in a list to a given argument, and gather the Just values.

rfWarnings' :: RuleFile -> [String]
rfWarnings' rf = catMaybes $ map ($rf) warnings
 where warnings = [ \rf -> guard (length (neigh rf) > 3) >> return
                             "warning: unknown extra information in the neighborhood, discarding it and moving on"
                  , {-more warnings-}
                  ]

To avoid repeating this guard stuff in the Maybe MonadPlus, you can define an operator that links ordinary predicates directly to the strings you want them to yield:

 where warnings = [ (\rf -> length (neigh rf) > 3) ==>
                             "warning: unknown extra information in the neighborhood, discarding it and moving on"
                  , {-more warnings-}
                  ]
       pred ==> str = guard pred >> return str

Looks perhaps even nicer the other way around:

 where warnings = [ "warning: unknown extra information in the neighborhood, discarding it and moving on"
                      \<= \rf -> length (neigh rf) > 3
                  , {-more warnings-}
                  ]
       str \<= pred = guard pred >> return str
like image 32
leftaroundabout Avatar answered Dec 05 '25 23:12

leftaroundabout



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!