Live data from Hacker News

Unit testing IO in Haskell

blog.pusher.com

1–10 of 11 posts

Re: Unit testing IO in Haskell

#4
This is a good post in that it illustrates how it's possible to write useful Haskell that interacts with the real world without weaving IO through the entire codebase (as many beginners end up doing).

I still find myself preferring to use a dependency injection style when writing Haskell. The code ends up looking similar, and more flexible, in my opinion (you can inject different dependencies based on runtime values).

Here is an example using the method described in the article vs. dependency injection to print out the current time.

Article:

    class MonadTime m where
        getTime :: m Integer

    class MonadPrint m a where
        doPrint :: a -> m ()

    instance MonadTime IO where
        getTime = do
            TOD s _  MonadPrint IO a where
        doPrint = print

    printTime :: (Monad m, MonadPrint m Integer, MonadTime m) => m ()
    printTime = getTime >>= doPrint

    main :: IO ()
    main = printTime
Dependency injected:

    data MonadPrint m a = MonadPrint { doPrint :: a -> m () }

    data MonadTime m = MonadTime { getTime :: m Integer }

    ioMonadTime :: IO Integer
    ioMonadTime = do
        TOD s _  MonadPrint m Integer -> MonadTime m -> m ()
    printTime MonadPrint{..} MonadTime{..} = getTime >>= doPrint

    main :: IO ()
    main = printTime (MonadPrint print) (MonadTime ioMonadTime)
 
    
Good read on the subject of being careful with overusing typeclasses:

http://www.haskellforall.com/2012/05/scrap-your-type-classes...

Re: Unit testing IO in Haskell

#5

This is a good post in that it illustrates how it's possible to write useful Haskell that interacts with the real world without weaving IO through the entire codebase (as many beginners end up doing). I still find myself preferring to use a dependency injection style when writing Haskell. The code ends up looking similar, and more flexible, in my opinion (you can inject different dependencies based on runtime values)…

Of course typeclasses can be viewed as dependency injection as well. It its just that with typeclasses the injection is implicit rather than explicit. But mechanically they are identical

Re: Unit testing IO in Haskell

#7
post #5

This is a good post in that it illustrates how it's possible to write useful Haskell that interacts with the real world without weaving IO through the entire codebase (as many beginners end up doing). I still find myself preferring to use a dependency injection style when writing Haskell. The code ends up looking similar, and more flexible, in my opinion (you can inject different dependencies based on runtime values)…

Of course typeclasses can be viewed as dependency injection as well. It its just that with typeclasses the injection is implicit rather than explicit. But mechanically they are identical

The main difference is that typeclasses are injected at compile time rather than run time. By being explicit, I can inject different behavior based on runtime values.

Re: Unit testing IO in Haskell

#8
post #5

Earlier quoted context omitted.

Of course typeclasses can be viewed as dependency injection as well. It its just that with typeclasses the injection is implicit rather than explicit. But mechanically they are identical

The main difference is that typeclasses are injected at compile time rather than run time. By being explicit, I can inject different behavior based on runtime values.

well, the typeclasses are 'determined' at compile-time, but they are certainly de-sugared into extra function paramaters that are still passed around (injected) at run-time. Haskell does not completely specialize this away (even though in theory it could). But i grant you manual threading allows the user to do different things than expected.

Re: Unit testing IO in Haskell

#9
post #8

Earlier quoted context omitted.

The main difference is that typeclasses are injected at compile time rather than run time. By being explicit, I can inject different behavior based on runtime values.

well, the typeclasses are 'determined' at compile-time, but they are certainly de-sugared into extra function paramaters that are still passed around (injected) at run-time. Haskell does not completely specialize this away (even though in theory it could). But i grant you manual threading allows the user to do different things than expected.

Not all typeclass instances can be determined at compile time!

Re: Unit testing IO in Haskell

#10
post #5

This is a good post in that it illustrates how it's possible to write useful Haskell that interacts with the real world without weaving IO through the entire codebase (as many beginners end up doing). I still find myself preferring to use a dependency injection style when writing Haskell. The code ends up looking similar, and more flexible, in my opinion (you can inject different dependencies based on runtime values)…

Of course typeclasses can be viewed as dependency injection as well. It its just that with typeclasses the injection is implicit rather than explicit. But mechanically they are identical

This is what I thought when I first saw the parent comment. It looks like an interesting alternative approach, but from the example I am not clear on why it is more flexible than the version using typeclasses.

I reckon I will need to re-implement my mocks using the technique you describe to really understand the motivation.

Post reply on HN