Hi Francesco,

You won't have a concrete type for every combination of constraints. For the most part, your programs will all end up in IO (or some transformer over IO) anyway. I usually have a "test" transformer and a "production" transformer (both of which are usually ReaderT Env IO), and instances defined for each. 

It's OK to pass an overly large record to a function. A common style is to write a huge `data Env = Env { ... }` with dozens of fields, and use classes to only access the things you need. This allows you to pass smaller Envs in, but it doesn't require that you write dozens of types preemptively.

I would suggest that `MonadTime` and `MonadKeys` are not great choice for a Layer 2 - it would be really difficult to write a mock for MonadKeys that was in any way meaningful. I'd suggest restricting these classes to be tightly coupled to your domain problem. Instead of getting random keys, try a sum-type with all the possible things you'd prompt the user for, and a type for possible user responses. This is much easier to mock meaningfully.

Matt Parsons


On Sun, May 19, 2019 at 4:25 AM Francesco Ariis <fa-ml@ariis.it> wrote:
(literate Haskell follows)

Hello -cafe,
    I recently read "Three Layer Haskell Cake" [1] and decided to give
it a go. I am stuck on a simple problem and feel like I am missing
something easy.
One of the first examples is a mocking of an `IO UTCTime` function.

>
> {-# Language FlexibleInstances #-}
>
> data UTCTime = UTCTime Int
>
> class MonadTime m where
>    getCurrentTime :: m UTCTime
>
> instance MonadTime ((->) UTCTime) where
>     getCurrentTime = id
>
> -- instance IO omitted
>

Everything is clear. I decided to write another function, mocking
a stream-of-Char input.

>
> class MonadKeys m where
>     getKeys :: m [Char]
>
> instance MonadKeys ((->) [Char]) where
>     getKeys = id
>
> -- instance IO omitted
>

Again, fine. But now, if I want to write a function which combines the
two external services, like:

>
> someFun :: (MonadTime m, MonadKeys m) => m ()
> someFun = undefined
>

there is no non-IO instance satisfying the two constraints. I could of
course write a type

>
> data Env = Env UTCTime [Char]
>

and make it instance of MonadTime and MonadKeys, but then I am passing
non-relevant arguments to a mocked function like getCurrentTime, which
would only need UTCTime.

What am I doing incorrectly?
-F


[1] https://www.parsonsmatt.org/2018/03/22/three_layer_haskell_cake.html


_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.