
Hello, I am wondering if this code can be made a bit nicer? I am checking if a file exists... and then if it exists I'm checking whether it's older than two hours from now. It is a bit annoying to have that second block in the else (i could make a second function but i think it all belongs in this function). This structure makes me think about the Maybe monad, where it would automatically exit after it would find out that the file doesn't exists, instead of having a cascade of if statements inside one another. But this function is already in the IO monad (maybe I'm thinking about this in the wrong way though). Any idea for an improvement? -- don't re-fetch if I fetched in the last 2 hours needToFetchAgain :: FilePath -> IO Bool needToFetchAgain export_filename = do exists <- doesFileExist export_filename if (not exists) then return False else do modif <- getModificationTime export_filename curTime <- getClockTime let diff = diffClockTimes curTime modif return $ tdSec diff >= (3600*2) -- 2 hours Thank you! Emmanuel

hmm otherwise the return False must be a return True, stupid bug:
-- don't re-fetch if I fetched in the last 2 hours
needToFetchAgain :: FilePath -> IO Bool
needToFetchAgain export_filename = do
exists <- doesFileExist export_filename
if (not exists)
then return True
else do
modif <- getModificationTime export_filename
curTime <- getClockTime
let diff = diffClockTimes curTime modif
return $ tdSec diff >= (3600*2) -- 2 hours
On Wed, Oct 31, 2012 at 11:25 AM, Emmanuel Touzery
Hello,
I am wondering if this code can be made a bit nicer?
I am checking if a file exists... and then if it exists I'm checking whether it's older than two hours from now. It is a bit annoying to have that second block in the else (i could make a second function but i think it all belongs in this function).
This structure makes me think about the Maybe monad, where it would automatically exit after it would find out that the file doesn't exists, instead of having a cascade of if statements inside one another.
But this function is already in the IO monad (maybe I'm thinking about this in the wrong way though).
Any idea for an improvement?
-- don't re-fetch if I fetched in the last 2 hours needToFetchAgain :: FilePath -> IO Bool needToFetchAgain export_filename = do exists <- doesFileExist export_filename if (not exists) then return False else do modif <- getModificationTime export_filename curTime <- getClockTime let diff = diffClockTimes curTime modif return $ tdSec diff >= (3600*2) -- 2 hours
Thank you!
Emmanuel

Hi Emmanuel, you could write monadic boolean operators (<&&>, <||>) and a "monadic" when (whenM) - I think both are in some package - to be able to write somwthing like: whenM (not <$> doesFileExist file <||> olderThan twoHours file) $ do ... import Control.Applicative ((<$>)) (<&&>) :: Monad m => m Bool -> m Bool -> m Bool (<&&>) m1 m2 = do r1 <- m1 if r1 then m2 else return False (<||>) :: Monad m => m Bool -> m Bool -> m Bool (<||>) m1 m2 = do r1 <- m1 if r1 then return True else m2 whenM :: Monad m => m Bool -> m () -> m () whenM p m = do r <- p if r then m else return () olderThan :: Int -> FilePath -> IO Bool olderThan secs file = do modif <- getModificationTime file curTime <- getClockTime let diff = diffClockTimes curTime modif return $ tdSec diff >= secs twoHours = 3600 * 2 'not <$> doesFileExist file' is a shorcut for: r <- doesFileExist file return $ not r But all of this would mostly only really pay off, if you have some file operations heavy code. Otherwise, all of these helper functions (operators) are quite generic. Obviously, I'm currently a bit bored ... Greetings, Daniel

On Wed, Oct 31, 2012 at 06:14:52PM +0100, Daniel Trstenjak wrote:
(<&&>) :: Monad m => m Bool -> m Bool -> m Bool (<&&>) m1 m2 = do r1 <- m1 if r1 then m2 else return False
(<||>) :: Monad m => m Bool -> m Bool -> m Bool (<||>) m1 m2 = do r1 <- m1 if r1 then return True else m2
That the operators behave like the boolean ones we should have the same fixity declarations: infixr 3 (<&&>) infixr 2 (<||>) Greetings, Daniel

Thank you that is very interesting... this is really impressive with haskell that it's so "easy" to add such operators, that they don't need to be builtin. Thanks for your time, it was not wasted! On Wed, Oct 31, 2012 at 6:26 PM, Daniel Trstenjak < daniel.trstenjak@gmail.com> wrote:
On Wed, Oct 31, 2012 at 06:14:52PM +0100, Daniel Trstenjak wrote:
(<&&>) :: Monad m => m Bool -> m Bool -> m Bool (<&&>) m1 m2 = do r1 <- m1 if r1 then m2 else return False
(<||>) :: Monad m => m Bool -> m Bool -> m Bool (<||>) m1 m2 = do r1 <- m1 if r1 then return True else m2
That the operators behave like the boolean ones we should have the same fixity declarations:
infixr 3 (<&&>) infixr 2 (<||>)
Greetings, Daniel
_______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners

On Thu, Nov 1, 2012 at 5:03 AM, Emmanuel Touzery
Thank you that is very interesting... this is really impressive with haskell that it's so "easy" to add such operators, that they don't need to be builtin.
This can't be emphasized enough to people starting out. Haskell doesn't
have operators in the same sense as other languages. All those punctuation
symbols are ordinary functions and can be user- or library-defined to mean
anything.
There are also primitive functions and functions defined via the FFI but
those aren't baked into the language either.
<tangential rant> There is a reason why the prelude is called as such and
not "the standard library", which is a term I don't think you can find in
the language specs. That said, people looking for "the standard library"
are usually directed to the Haskell Platform, again not because there _is_
such a thing, but because their needs are best met there.
-- Kim-Ee
On Thu, Nov 1, 2012 at 5:03 AM, Emmanuel Touzery
Thank you that is very interesting... this is really impressive with haskell that it's so "easy" to add such operators, that they don't need to be builtin.
Thanks for your time, it was not wasted!
On Wed, Oct 31, 2012 at 6:26 PM, Daniel Trstenjak < daniel.trstenjak@gmail.com> wrote:
On Wed, Oct 31, 2012 at 06:14:52PM +0100, Daniel Trstenjak wrote:
(<&&>) :: Monad m => m Bool -> m Bool -> m Bool (<&&>) m1 m2 = do r1 <- m1 if r1 then m2 else return False
(<||>) :: Monad m => m Bool -> m Bool -> m Bool (<||>) m1 m2 = do r1 <- m1 if r1 then return True else m2
That the operators behave like the boolean ones we should have the same fixity declarations:
infixr 3 (<&&>) infixr 2 (<||>)
Greetings, Daniel
_______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners
_______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners

On Thu, Nov 1, 2012 at 11:58 AM, Kim-Ee Yeoh
<tangential rant> There is a reason why the prelude is called as such and not "the standard library", which is a term I don't think you can find in the language specs.
The word "standard" does not appear as such, but it can be inferred... http://www.haskell.org/onlinereport/haskell2010/haskellpa2.html#x20-192000II Haskell 2010 Language Report: The Haskell 2010 Libraries Note however that the language of the Report's preface applies: this is not intended to be a standard library in the sense usually meant by newcomers, but a conservative minimum required by all conformant implementations. (It's also not limited to the Prelude; neither was Haskell98's version.) -- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix/linux, openafs, kerberos, infrastructure http://sinenomine.net

On Wed, Oct 31, 2012 at 10:44 PM, Daniel Trstenjak < daniel.trstenjak@gmail.com> wrote:
(<||>) :: Monad m => m Bool -> m Bool -> m Bool (<||>) m1 m2 = do r1 <- m1 if r1 then return True else m2
We can do some algebraic simplification, using the law *if P then True else Q *is same as *P || Q* : (<||>) m1 m2 = do r1 <- m1 r2 <- m2 return (r1 || r2) Which reminds me of the definition of liftM2 (from Control.Monad): liftM2 f m1 m2 = do { x1 <- m1; x2 <- m2; return (f x1 x2) } And so we can golf down to: (<||>) = liftM2 (||) Now, instead of using liftM2 from Control.Monad, I could as well use liftA2 from Control.Applicative : (<||>) = liftA2 (||) So my questions to the experts: 1> Are these equivalent or am I missing something? 2> Any thumb rules on when to use Control.Monad and when to use Control.Applicative? Rusi -- http://blog.languager.org

I think you are mistaken, if I understood right one of the key properties
of monads is to ensure order of execution, therefore if you do:
r1 <- m1
r2 <- m2
you are going to run both m1 and m2 (and in this order).
and indeed:
---------
import Control.Monad
(<||>) = liftM2 (||)
f1 = do
putStrLn "f1"
return True
f2 = do
putStrLn "f2"
return True
main = do f1 <||> f2
---------
output is:
f1
f2
On Sat, Nov 3, 2012 at 9:37 AM, Rustom Mody
On Wed, Oct 31, 2012 at 10:44 PM, Daniel Trstenjak < daniel.trstenjak@gmail.com> wrote:
(<||>) :: Monad m => m Bool -> m Bool -> m Bool (<||>) m1 m2 = do r1 <- m1 if r1 then return True else m2
We can do some algebraic simplification, using the law *if P then True else Q *is same as *P || Q* :
(<||>) m1 m2 = do r1 <- m1 r2 <- m2 return (r1 || r2)
Which reminds me of the definition of liftM2 (from Control.Monad):
liftM2 f m1 m2 = do { x1 <- m1; x2 <- m2; return (f x1 x2) }
And so we can golf down to: (<||>) = liftM2 (||)
Now, instead of using liftM2 from Control.Monad, I could as well use liftA2 from Control.Applicative :
(<||>) = liftA2 (||)
So my questions to the experts:
1> Are these equivalent or am I missing something? 2> Any thumb rules on when to use Control.Monad and when to use Control.Applicative?
Rusi -- http://blog.languager.org
_______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners

Hi Rusi,
We can do some algebraic simplification, using the law *if P then True else Q *is same as *P || Q* :
(<||>) m1 m2 = do r1 <- m1 r2 <- m2 return (r1 || r2)
1> Are these equivalent or am I missing something?
Your operator doesn't behave in the same way, because r2 is computed, even if r1 is true. *Main> (putStrLn "m1" >> return True) <||> (putStrLn "m2" >> return True) m1 m2 True
2> Any thumb rules on when to use Control.Monad and when to use Control.Applicative?
They're distinct type classes. So the question in this case is rather, for which type class you need an operator. When writing your own abstractions, than Applicative seems to be more appropriate, if you have a more combinatorial abstraction, like for parsers or HTML form fields. But I'm also still trying to get a feeling for this. Greetings, Daniel

Hi Emmanuel, On Wed, Oct 31, 2012 at 11:25:05AM +0100, Emmanuel Touzery wrote:
But this function is already in the IO monad (maybe I'm thinking about this in the wrong way though).
A huge step in understanding monads for myself was to realize, that you can put any monadic computation (IO is the exception) inside an other monadic computation. e.g. ioComputation :: IO () ioComputation = do let maybe = do maybeComputation1 maybeComputation2 ... return () Most monads have some kind of execution function, like the state monad (runState, evalState): ioComputation :: IO () let initalState = ... result = evalState $ do stateComputation1 stateComputation2 initalState return () In your case the Maybe monad doesn't help that much, because the functions you're calling doesn't return a Maybe value. In your case, why having a 'needToFetchAgain' and probably a 'fetch' function instead of just a 'refetch' function, which encapsulates the whole fetching? Greetings, Daniel

Thank you, I didn't realize I can simply put the maybe computation inside the IO as you've shown. And I must research runState/evalState a bit more. But yes it doesn't map so well to maybe. The parrallel just came to my mind. The reason for splitting needToFetchAgain out was to keep function size small and the code readable. And in the caller function I can nicely write: needToFetchAgain <- needToFetchAgain export_filename when needToFetchAgain $ do .... 'when' coming from Control.Monad. Well so I'll leave this function as it is for now and make a note to read some more about runState/evalState. Thank you! Emmanuel On Wed, Oct 31, 2012 at 12:05 PM, Daniel Trstenjak < daniel.trstenjak@gmail.com> wrote:
Hi Emmanuel,
On Wed, Oct 31, 2012 at 11:25:05AM +0100, Emmanuel Touzery wrote:
But this function is already in the IO monad (maybe I'm thinking about this in the wrong way though).
A huge step in understanding monads for myself was to realize, that you can put any monadic computation (IO is the exception) inside an other monadic computation.
e.g.
ioComputation :: IO () ioComputation = do let maybe = do maybeComputation1 maybeComputation2 ... return ()
Most monads have some kind of execution function, like the state monad (runState, evalState):
ioComputation :: IO () let initalState = ... result = evalState $ do stateComputation1 stateComputation2 initalState return ()
In your case the Maybe monad doesn't help that much, because the functions you're calling doesn't return a Maybe value.
In your case, why having a 'needToFetchAgain' and probably a 'fetch' function instead of just a 'refetch' function, which encapsulates the whole fetching?
Greetings, Daniel
_______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners

On Oct 31, 2012, at 3:25 AM, Emmanuel Touzery wrote:
I am checking if a file exists... and then if it exists I'm checking whether it's older than two hours from now. It is a bit annoying to have that second block in the else (i could make a second function but i think it all belongs in this function).
Check out this section of Real World Haskell. It should give you ideas. http://book.realworldhaskell.org/read/io-case-study-a-library-for-searching-...
participants (6)
-
Brandon Allbery
-
Daniel Trstenjak
-
Emmanuel Touzery
-
Kim-Ee Yeoh
-
Rustom Mody
-
Sean Perry