option icon indicating copy to clipboard operation
option copied to clipboard

added threading operator

Open bmuller opened this issue 12 years ago • 3 comments

Just for funzies...

bmuller avatar Mar 27 '13 22:03 bmuller

ACK.

Cool man, thanks! I am going to need a bit to dig into this; a bit slammed right now. A few initial thoughts/questions:

  • I am assuming this is something akin to Clojure's -> and ->> macros? (I believe the equivalent in Haskell being something like composition under point-free syntax x = f . g). Amirite on this?
  • A while back I did a thing on coderwall: https://coderwall.com/p/shjfkg. Basically it brings composition to Proc. In this case you would be able to compose a bunch of lambdas and then just pass off to map. That would be another way to go about this but suffers on the syntax side.

Again, just want to clarify the purpose here. I like the concept a lot just want to make sure we are adding the right thing in the right place.

rares avatar Mar 28 '13 03:03 rares

Haha - I'm not sure if it's "the right thing in the right place" as much as me just screwing around.

Yeah - just like threading in Clojure - except that each intermediate value is an Option as well, meaning that any method in the threading could potentially return a nil, producing a NoneClass, so that the rest of the operations won't fail.

I've thought about doing something like this before...but maybe make it less haskelly and amenable to enumerables, like:

# anarray could be empty, but if it has at least one element,
# then the block will be called with it
Option(anarray).if_first { |one| ... }

# or, alternately something like
Option(anarray).if_not(:empty?) { |a| ... }

# and then allow for threading per element
Option([" 1 ", " 2 ", " 3 "]).thread(:strip, :to_i, :next)
# => [2,3,4]

# which is way prettier and efficient than
[" 1 ", " 2 ", " 3 "].map(&:strip).map(&:to_i).map(&:next)
# and at least way prettier than
[" 1 ", " 2 ", " 3 "].map { |i| i.strip.to_i.next }

# PLUS if any of the intermediate method calls returns nil, then you still get a result, like
Option([" 1 ", " 2 ", " 3 "]).thread(:strip, :make_a_nil, :to_i, :next)
#=> [<NoneClass instance>, <NoneClass instance> <NoneClass instance>]

bmuller avatar Mar 28 '13 04:03 bmuller

Ok, I see where you are going with the Enumerable integration but I don't think that is something I would want to add to Option. I think maybe a companion lib that patches a few methods in to Enumerable might be interesting:

 def lift_option
   map { |v| Option(v) }
 end

or

 def first_option
   Option(first)
 end

 def last_option
   Option(last)
 end

As for the proposed feature itself, I want to add it but first a few questions/requests:

  1. A complete failure on my part for not mentioning it in the contribution guidelines but could you just add some coverage specs for both Some and None in spec/option_spec.rb? Nothing crazy, I am just looking for the fact that the 2 states of the Option are doing 2 different things.
  2. The name. thread (although technically thread is just a nickname for the -> macro) is fine in clojure because you you deal with higher-level concurrency abstractions but I fear some ambiguity in the ruby world (as Thread is pretty much it wrt to concurrency). What do you think of either 'weave' or 'sequence'?
  3. Not super important but I would be partial to Some#thread implemented as a recursive call to either map or flat_map followed by the tail call to thread. Not a biggie. I'll still take it without this.

Anyways, thanks for the work and let me know what you think about item number 2.

rares avatar Apr 01 '13 17:04 rares