<div dir="ltr"><div>Hi Amit!<br><br>I was initially a little confused by having both a `tr` type variable and a `r` type variable. Pretty sure that's a typo and they're intended to be the same. Otherwise you'd need to pass in something like `Proxy r` so that the usage of saveToDb constrains all the type variables.<br><br>ClassyPrelude has a good solution to this problem, where you want to constrain the type of the container but not explicitly describe its contents. Check out this section: <a href="https://hackage.haskell.org/package/classy-prelude-0.12.4/docs/ClassyPrelude.html#g:27" target="_blank">https://hackage.haskell.org/package/classy-prelude-0.12.4/docs/ClassyPrelude.html#g:27</a><br><br>In this case, you'd use "asList":</div><div><br></div><div><div>asList :: [a] -> [a]</div><div>asList = id</div><div><br>And then use this like so: `forM_ (asList rows) processRow`<br><br>Alternatively, I think this will work if you enable PartialTypeSignatures (new in 7.10): `forM_ (rows :: [_]) processRow`</div><br></div><div>-Michael</div><div class="gmail_extra"><br><div class="gmail_quote">On Thu, Oct 15, 2015 at 1:17 PM, Amit Aryeh Levy <span dir="ltr"><<a href="mailto:amit@amitlevy.com" target="_blank">amit@amitlevy.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">Thanks Adam!<br>
<br>
Vector does make more sense (i'll continue to use lists in this thread<br>
just for simplicity, since I don't think it matters for the higher level<br>
problem).<br>
<br>
`forM_ (objs :: Type) ....` seems like exactly the right solution in the<br>
simple case. However, it doesn't seem to work if I try to write a more<br>
general function. For example:<br>
<br>
```<br>
class ToRow r<br>
<br>
saveToDb :: FromJSON r, ToRow r => ByteString -> (tr -> IO ()) -> IO ()<br>
saveToDb json processRow =<br>
case eitherDecode json of<br>
Left err => return () -- for simplicity<br>
Right rows => forM_ (rows :: [r]) processRow<br>
```<br>
<br>
GHC complains about two things:<br>
<br>
1. eitherDecode can't determine which `FromJSON` instance to use<br>
2. "Couldn't match expected type [r1] with actual type a0" in `rows<br>
:: [r]`.<br>
<br>
I think the issue is that GHC is not relating `rows :: [r]` to `FromJSON<br>
r` in the function type.<br>
<br>
Falling back to either ScopedTypeVariables or explicit<br>
contruction/deconstruction of the list works:<br>
<br>
```<br>
class ToRow r<br>
<br>
saveToDb :: FromJSON r, ToRow r => ByteString -> (tr -> IO ()) -> IO ()<br>
saveToDb json processRow =<br>
case eitherDecode json of<br>
Left err => return () -- for simplicity<br>
Right (rows :: [r]) => forM_ rows processRow<br>
```<br>
<br>
Thoughts?<br>
<br>
Thanks!<br>
<span><font color="#888888">Amit<br>
</font></span><br>
P.S.<br>
Thanks to Felipe for politely reminding me that these are lists we are<br>
dealing with, not arrays!<br>
<div><div><br>
On 10/15/2015 02:27 PM, Adam Bergmark wrote:<br>
> If you care about performance you may - I haven't benchmarked - want to use<br>
> Vector instead of lists here since that's what aeson uses internally. Then<br>
> it's pretty handy that you can still use forM_.<br>
><br>
> It's possible that the list pattern deconstruction and list construction<br>
> gets optimized away, my gut says you need -O2 for that to happen. Here's a<br>
> good explanation on how to dump and read core so you can check for yourself<br>
> what happens in this case:<br>
> <a href="http://stackoverflow.com/questions/6121146/reading-ghc-core" rel="noreferrer" target="_blank">http://stackoverflow.com/questions/6121146/reading-ghc-core</a> . Either way<br>
> it's definitiely not less efficient to annotate the type instead. You don't<br>
> need ScopedTypeVariables here, you can write the type inside an expression<br>
> instead: `forM (objs :: Type) [...]`<br>
><br>
> HTH,<br>
> Adam<br>
><br>
><br>
> On Thu, Oct 15, 2015 at 7:16 PM, Amit Aryeh Levy <<a href="mailto:amit@amitlevy.com" target="_blank">amit@amitlevy.com</a>> wrote:<br>
><br>
>> I've been running into a relatively small but frequent annoyance with<br>
>> base >= 4.8 (GHC 7.10). `Control.Monad.foldM_`, `Control.Monad.mapM_`<br>
>> and `Control.Monad.forM_` are generalized traverse over any `Foldable a`<br>
>> rather than just arrays (`[a]`).<br>
>><br>
>> This is great, except I'm finding that, for a lot of my code that works<br>
>> well in previous versions, I need to specialize the argument to `[a]`<br>
>> now. If other people are encoutering a similar patter, I wonder what are<br>
>> your best practices for doing this: ScopedTypeVariables? Deconstruct the<br>
>> reconstruct the array? ...<br>
>><br>
>> The most common example is when I deserialize a JSON array with aeson<br>
>> and want to traverse over that array (say, to store the objects to a DB):<br>
>><br>
>> ```<br>
>> let objArray = eitherDecode myjson<br>
>> case objArray of<br>
>> Left err -> ...<br>
>> Right (objs :: [MyObjType]) -><br>
>> forM_ objs $ \obj -> saveToDb obj<br>
>> ```<br>
>><br>
>> The above fix requires `ScopedTypeVariables` (which is probably OK).<br>
>> Another option is to deconstruct and reconstruct the list:<br>
>><br>
>> ```<br>
>> Right (o:objs) -><br>
>> forM_ (o:objs) $ \obj -> saveToDb obj<br>
>> ```<br>
>><br>
>> Does this get optimized away?<br>
>><br>
>> Penny for your thoughts?<br>
>><br>
>> Cheers!<br>
>> Amit<br>
>><br>
>><br>
>> _______________________________________________<br>
>> Haskell-Cafe mailing list<br>
>> <a href="mailto:Haskell-Cafe@haskell.org" target="_blank">Haskell-Cafe@haskell.org</a><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>
>><br>
>><br>
<br>
<br>
</div></div><br>_______________________________________________<br>
Haskell-Cafe mailing list<br>
<a href="mailto:Haskell-Cafe@haskell.org" target="_blank">Haskell-Cafe@haskell.org</a><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>
<br></blockquote></div><br></div></div>