Are objects Evil now?


(Andy Wootton) #21

“Objects are closures with multiple methods,
closures are objects with a single method.
So, yes [OOP and FP can be used together.]” – Erik Meijer

It sounds very wise but isn’t it recursive? :slight_smile:

And here’s a truly appalling description of OOP


(Marc Cooper) #22

Going back up the discussion stack a bit:

Streams are composable, lazy enumerables that generate items one-by-one during enumeration. So, a range 1..10 is a stream. This can be composed with any other stream to produce a stream:

1..999_999_999
|> Stream.map(&(&1 * 3))
#Stream<[enum: 1..5, funs: [#Function<47.1220793/1 in Stream.map 2>]]>

and so on.

1..999_999_999
|> Stream.map(&(&1 * 3))
|> Stream.filter(odd?)
#Stream<[enum: 1..5,
funs: [#Function<47.122079345/1 in Stream.map/2>,
#Function<40.122079345/1 in Stream.filter/2>]]>

Nothing is evaluated until an Enum function is met (or Stream.run/1)

1..999_999_999
|> Stream.map(&(&1 * 3)) 
|> Stream.filter(odd?) 
|> Enum.take(2)
[3, 9]

Note that the above returns immediately, whereas changing the filter step to Enum.filter(odd?) results in a very, very long wait.

Another benefit is that the intermediate collections are not built. The streams compose into a single function that operates on each item or until a stop condition is met.

That last is how you can handle an infinite collection/stream (using, say, Stream.iterate/2)

Stream.iterate(2, &(&1 + 2))
|> Enum.take(5)
[2, 4, 6, 8, 10]

If I’d used a function from elsewhere, above, it doesn’t need to be lazy. It’s just a function. So it doesn’t “break” the stream.

do_it = &(&1 * 3)
#Function<6.99386804/1 in :erl_eval.expr/5>

1..999_999_999
|> Stream.map(do_it) 
|> Stream.filter(odd?) 
|> Enum.take(2)
[3, 9]

It’s no problem outputting to the console:

1..999_999_999
|> Stream.map(&(&1 * 3))
|> Stream.filter(odd?)
|> Stream.map(&IO.inspect/1)
|> Enum.take(2)
3
9
[3, 9]

The way I think of it is a series of vertical process, one for each item, rather than horizontally processing each statement line by line.

Fun stuff.


(Marc Cooper) #23

The article doesn’t mention pipeline, so whatever you were enjoying, appears to have been orthogonal :open_mouth:

Hickey makes a strange comments in that article: “stream code is ugly and imperative”. Mate, it’s your language: so don’t make it ugly and don’t make it imperative.

From his comments, he seemed to struggle to build streams without breaking what he’d already built.

Can’t say I like his solution, but it’s not much different to elixir’s approach. There’s just a lot more brackets and, imo, absence of clarity.


(Andy Wootton) #24

It says at the top that he was experimenting with streams as an alternative and decided not to go there. As I don’t know what they were, or what they were an alternative to, I’m willing to believe he was right.

Carin Myers Preface xi, says:
"Don’t Worry About the Parens
A common initial concern from people looking at Clojure is about the parentheses.

Don’t worry about the parens.

Really

The simplicity and elegance that the parens structure gives to Clojure is one of its main advantages."

Code is a list, so code is data which can transform itself. If any software is going to self-actualise, I bet it’s written in a Lisp. I’m not saying that’s a good thing.


(Marc Cooper) #25

Clojure has streams, but not as a separate type. They’ve been implemented in sequences. That’s a design decision, and I’m sure it was the right one. But, as Hickey says himself, he still had to make compromises with mutation.


(Andy Wootton) #26

I like this explanation of OO vs Clojure(FP?) metaphysics
http://www.braveclojure.com/zombie-metaphysics/

(May contain Ruby)

@auxbus Is this time-ordered sequence of values for the state of a sequence (any Clojure collection) what you meant by a stream (in Clojure?)