<div dir="ltr">Hi Francesco,<div><br>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. </div><div><br></div><div>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.</div><div><br></div><div>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.</div><div><br></div><div><div><div dir="ltr" class="gmail_signature" data-smartmail="gmail_signature"><div dir="ltr"><div>Matt Parsons</div></div></div></div><br></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Sun, May 19, 2019 at 4:25 AM Francesco Ariis <<a href="mailto:fa-ml@ariis.it">fa-ml@ariis.it</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">(literate Haskell follows)<br>
<br>
Hello -cafe,<br>
I recently read "Three Layer Haskell Cake" [1] and decided to give<br>
it a go. I am stuck on a simple problem and feel like I am missing<br>
something easy.<br>
One of the first examples is a mocking of an `IO UTCTime` function.<br>
<br>
><br>
> {-# Language FlexibleInstances #-}<br>
><br>
> data UTCTime = UTCTime Int<br>
><br>
> class MonadTime m where<br>
> getCurrentTime :: m UTCTime<br>
><br>
> instance MonadTime ((->) UTCTime) where<br>
> getCurrentTime = id<br>
><br>
> -- instance IO omitted<br>
><br>
<br>
Everything is clear. I decided to write another function, mocking<br>
a stream-of-Char input.<br>
<br>
><br>
> class MonadKeys m where<br>
> getKeys :: m [Char]<br>
><br>
> instance MonadKeys ((->) [Char]) where<br>
> getKeys = id<br>
><br>
> -- instance IO omitted<br>
><br>
<br>
Again, fine. But now, if I want to write a function which combines the<br>
two external services, like:<br>
<br>
><br>
> someFun :: (MonadTime m, MonadKeys m) => m ()<br>
> someFun = undefined<br>
><br>
<br>
there is no non-IO instance satisfying the two constraints. I could of<br>
course write a type<br>
<br>
><br>
> data Env = Env UTCTime [Char]<br>
><br>
<br>
and make it instance of MonadTime and MonadKeys, but then I am passing<br>
non-relevant arguments to a mocked function like getCurrentTime, which<br>
would only need UTCTime.<br>
<br>
What am I doing incorrectly?<br>
-F<br>
<br>
<br>
[1] <a href="https://www.parsonsmatt.org/2018/03/22/three_layer_haskell_cake.html" rel="noreferrer" target="_blank">https://www.parsonsmatt.org/2018/03/22/three_layer_haskell_cake.html</a><br>
<br>
<br>
_______________________________________________<br>
Haskell-Cafe mailing list<br>
To (un)subscribe, modify options or view archives go to:<br>
<a href="http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe" rel="noreferrer" target="_blank">http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe</a><br>
Only members subscribed via the mailman list are allowed to post.</blockquote></div>