Skip to content
This repository has been archived by the owner on Jun 3, 2023. It is now read-only.

More idiomatic Ruby #1

Open
booch opened this issue Oct 10, 2014 · 6 comments
Open

More idiomatic Ruby #1

booch opened this issue Oct 10, 2014 · 6 comments

Comments

@booch
Copy link

booch commented Oct 10, 2014

Not to be a Debby Downer, but this doesn't feel at all like idiomatic Ruby.

I think the biggest improvement would be to mix in a transduce method into Enumerable, with a signature closer to Enumerable#reduce. The other thing that feels wrong is that almost everything is a class method. It seems like a more idiomatic way to do it would be with a block using instance_eval, so we could do something like:

(1..100).transduce([], :<<) do
  filter(:even?)
  map{ |n| n * 2 }
  take(5)
end

For composabilitiy:

first_5_even_doubled = Transducer.compose do
  filter(:even?)
  map{ |n| n * 2 }
  take(5)
end

(1..100).transduce([], :<<) do
  first_5_even_doubled
end

I'll have to think about this some more, and see if I can contribute some code back to implement a more idiomatic way of doing this in Ruby, while keeping composability in mind.

@booch
Copy link
Author

booch commented Oct 10, 2014

LOL. Just noticed who wrote all the code. Maybe I should criticize the specs while I'm at it. ;)

@dchelimsky
Copy link
Contributor

Hey @booch - thanks for the suggestions!

One thing to keep in mind is that we've got transducer libs for other langs, some OO (e.g. java), and some FP (e.g. Clojure) and need to consider the alignment of APIs across languages as we make API decisions. Not saying that alignment always trumps paradigm or lang-specific idioms, but there will likely be some tradeoffs.

Also, it's early days and I don't want to do too much that will box us into a corner later. For example, building a DSL around a block passed to transduce means we can't use a block to define the ground reducer, e.g. transduce(transducer, collection) {|result,input| ...}, which is something I think I'd like to support. If we decide to mix the transducer method onto Enumerable, then we could end up with something like collection.transduce(transducer, initial_value) {|result, input| ...}, which better aligns with collection.reduce {|result,input| ...}. Not saying this is where we're going, but focusing on the underlying structure first allows the surface to evolve later.

We're not accepting PR's (see https://github.com/cognitect-labs/transducers-ruby#contributing), but I'll take these ideas into consideration and credit you in any resulting commits (and reference them from this issue as well).

Thanks!

@fxposter
Copy link

@dchelimsky I'd suggest using module_function for things like compose, transduce, so that we can include/extend Transducers into modules or into global state. If you currently do that - you'll have map or filter, but compose will not be there.

@dchelimsky
Copy link
Contributor

@fxposter I'm curious about your use case - why do you want to include/extend with the Transducers module? I'm not sure it's something we want to encourage or officially support.

Regardless, I got the same effect and some other benefits in 03fd045.

@fxposter
Copy link

Regardless, I got the same effect and some other benefits in 03fd045.

Yep, extend self is basically the same (except that the compose/transduce/etc will be public when Transducers module is included somewhere). Thanks!

Why do I want this - because I'd like to have a cleaner api than writing Transducers.* or even T.* everywhere. It's much more convenient to have map/filter within object's/class's methods.

@fxposter
Copy link

And the ability to make code more convenient is basically free, ie: you don't need to patch any global state, you don't loose anything for that convenience :)

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants