One thing to consider here is that ecto allows you to attach the preload to the 
query itself. So instead of doing:

Repo.paginate(query) |> Repo.preload(:user)

You can do:

query |> Ecto.Query.preload(:user) |> Repo.paginate()

and everything will work correctly. Yes, this does not solve all the cases, but 
should cover the vast majority.

Michał.
On 21 May 2019, 19:15 +0200, Benjamin Milde <[email protected]>, wrote:
> Take for example https://github.com/drewolson/scrivener_ecto
>
> With a current version of ecto you can happily do this:
>
> ```
> Repo.all(query) |> Repo.preload(:user)
> Repo.one(query) |> Repo.preload(:user)
> ```
>
> but not this:
>
> ```
> Repo.paginate(query)  |> Repo.preload(:user)
> ```
>
> Given that doing some work on results of db calls is quite common I tend to 
> end up with lot's of functions pattern matching if results are 
> `%Scrivener.Page{}`, a list or an element. Say you also use 
> https://github.com/duffelhq/paginator for infinite scrolling pages you might 
> end up with yet another "collection" type struct, which implements Enumerable 
> so displaying items in views is super easy, but with no simple solution on 
> how to handle preloads or mappings.
>
> I've also tried using `Map.update!(elements, :key_of_items, function)` to 
> normalize stuff down to handling lists for those places using pagination, but 
> now I'm wrapping a lot of computation in those updates.
>
> It's also not just mapping, but I might also e.g. want to intersperse items 
> with separators or zip some additional data into the resultset and still not 
> lose the pagination information attached to those entries.
>
> My naive approach was that I can already use Collectable to fill items back 
> into lists as well as those pagination structs, but while for list and maps 
> it's easy to use `Enum.into([])` or `Enum.into(%{})` it's not as easy to get 
> a hold of an empty version of one of those pagination structs.
>
> I'm not super into all the theoretic types in functional programming. I think 
> I understand what a functor is, but I'm also not super certain I understand 
> what you're pointing at.
>
> Am Dienstag, 21. Mai 2019 18:36:54 UTC+2 schrieb José Valim:
> > > All the pagination libs I know for ecto return structs with additional 
> > > metadata besides the actual results of the db query. Currently one needs 
> > > to build around those structs specifically to be able to do preloads or 
> > > other mapping operations or one would loose the metadata.
> >
> > Can you please provide an example? I am just trying to full understand the 
> > problem. :)
> >
> > > Having a `SomeProtocol.empty` protocol we could still use everything 
> > > provided by Enum (even with it returning a list), but have a way to 
> > > replace items in the container with the new modified ones
> >
> > Wouldn't a functor be better suited then?
> >
> >
> > José Valim
> > www.plataformatec.com.br
> > Skype: jv.ptec
> > Founder and Director of R&D
> >
> >
> > > On Tue, May 21, 2019 at 6:34 PM Benjamin Milde <[email protected]> 
> > > wrote:
> > > > All the pagination libs I know for ecto return structs with additional 
> > > > metadata besides the actual results of the db query. Currently one 
> > > > needs to build around those structs specifically to be able to do 
> > > > preloads or other mapping operations or one would loose the metadata. 
> > > > Having a `SomeProtocol.empty` protocol we could still use everything 
> > > > provided by Enum (even with it returning a list), but have a way to 
> > > > replace items in the container with the new modified ones without 
> > > > manually pattern matching between single results, lists of results or 
> > > > structs returned because one happens to be using pagination. Monadic 
> > > > collection types basically don't do it much differently. Extract the 
> > > > subject out of the container, do computation and replace the old value 
> > > > when done. I'd imagine `Enumerable` and the potential `SomeProtocol` 
> > > > would solve a lot of usecases, where people usually ask for Enum to 
> > > > retain the outer collection type.
> > > >
> > > > Am Dienstag, 21. Mai 2019 18:14:49 UTC+2 schrieb José Valim:
> > > > > My concern is that Collectable.empty cannot be implemented by all 
> > > > > structs. For example, what does it mean to call empty() on a 
> > > > > IO.stream? Perhaps it would make sense as a separate protocol?
> > > > >
> > > > > Also, can you please expand on how Repo.preload could use 
> > > > > Scrivener.Page in detail? Thanks!
> > > > >
> > > > >
> > > > > José Valim
> > > > > www.plataformatec.com.br
> > > > > Skype: jv.ptec
> > > > > Founder and Director of R&D
> > > > >
> > > > >
> > > > > > On Tue, May 21, 2019 at 5:46 PM Benjamin Milde 
> > > > > > <[email protected]> wrote:
> > > > > > > I've had quite often needs for doing some computation (mapping) 
> > > > > > > on ecto result, but having a flexible function similar to 
> > > > > > > `Repo.preload` currently needs a whole bunch of boilerplate in 
> > > > > > > terms of differenciating collections from single items and 
> > > > > > > especially returning the same type afterwards (e.g. keep it a 
> > > > > > > collection or single item). I know that Enumerable and 
> > > > > > > Collectable were split consciously, but it would be great to have 
> > > > > > > something like `Collectable.empty/1`, so one could do some work 
> > > > > > > using `Enum` functions and in the end do: `Enum.into(changed, 
> > > > > > > Collectable.empty(initial))` and it would empty the collectable 
> > > > > > > and fill it up again using the changed data. This way the 
> > > > > > > `Repo.preload` could e.g. additionally support custom enumerable 
> > > > > > > and collectable collections as first argument like e.g. 
> > > > > > > `%Scrivener.Page{}`.
> > > > > > > --
> > > > > > > You received this message because you are subscribed to the 
> > > > > > > Google Groups "elixir-lang-core" group.
> > > > > > > To unsubscribe from this group and stop receiving emails from it, 
> > > > > > > send an email to [email protected].
> > > > > > > To view this discussion on the web visit 
> > > > > > > https://groups.google.com/d/msgid/elixir-lang-core/19c24b83-fc2a-4af3-950e-eba7f767db14%40googlegroups.com.
> > > > > > > For more options, visit https://groups.google.com/d/optout.
> > > > --
> > > > You received this message because you are subscribed to the Google 
> > > > Groups "elixir-lang-core" group.
> > > > To unsubscribe from this group and stop receiving emails from it, send 
> > > > an email to [email protected].
> > > > To view this discussion on the web visit 
> > > > https://groups.google.com/d/msgid/elixir-lang-core/1498525e-30fd-4cde-92a0-4b2e01cdbdf3%40googlegroups.com.
> > > > For more options, visit https://groups.google.com/d/optout.
> --
> You received this message because you are subscribed to the Google Groups 
> "elixir-lang-core" group.
> To unsubscribe from this group and stop receiving emails from it, send an 
> email to [email protected].
> To view this discussion on the web visit 
> https://groups.google.com/d/msgid/elixir-lang-core/5dff9895-1921-4794-89b9-d4ff45578880%40googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

-- 
You received this message because you are subscribed to the Google Groups 
"elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/elixir-lang-core/52f1de58-9b8e-49ef-98b3-e0b477dd08ae%40Spark.
For more options, visit https://groups.google.com/d/optout.

Reply via email to