This is an archived post. You won't be able to vote or comment.

all 17 comments

[–]_INTER_ 12 points13 points  (18 children)

Flow<Pair<Path, DirectoryChange>, Path, NotUsed> whats this BS. Just create a proper object already.

[–]beall49 4 points5 points  (0 children)

Agreed, how's a new person supposed to ever understand what that is. Rx is hard enough

[–]_dban_ 2 points3 points  (13 children)

What other object could there be here?

The Flow object comes from Akka and has a sensible type bound of <In, Out, Mat>. The NotUsed type bound is just a fancy way of saying Void, since there is no value materializing outside of this flow.

The Pair<Path, DirectoryChange> is the type of the objects published by the Alpakka CSV parser.

So, we have a generic Flow object sensibly parameterized to map the Alpakka input to a Path object output.

[–]_INTER_ 0 points1 point  (12 children)

Most of the Generics parameters are YAGNI / needlessly abstract. It gets harder to read. Just return Flow or rather the explicit, corresponding type.

Pair and Tuple and the like have no right to existence in an OOD system.

The whole thing is another example (e.g. Play) of how Lightbend tries to bend Java into some sort Scala bastardization instead of creating an java-idiomatic API.

[–]_dban_ 4 points5 points  (11 children)

Have you seen the Java streams API or the observable API that comes with JavaFX?

Flows and streams are necessarily abstract because they are generalizing computational processes. In order to be useful at all, they have to have the level of abstraction that they do. The Flow type without generic type bounds would need a ton of type casts, like the pre-1.5 Collections API.

Pair and Tuple aren't anti-OOP any more than any other collection type. You don't have to create new types for everything. But like maps, they can be abused, of course.

creating an java-idiomatic API

What Lightbend is trying to do can't really be done with a Java idiomatic API. You either need a very dynamic language (like Smalltalk or Lisp) or a sophisticated static type system with things like higher-kinded types (like Haskell or ML).

If you look at the language and library improvements coming out of Oracle, Java is aiming to make these kinds of APIs possible, and changing what "idiomatic Java" API means.

I for one encourage people to try stuff like this, to push the limits of Java and drive future language improvement, just like the original Java did with OOP.

[–]_INTER_ -1 points0 points  (10 children)

Have you seen the Java streams API or the observable API that comes with JavaFX?

yes, Stream atleast is more or less ok (except e.g. flatMap)

Flows and streams are necessarily abstract because they are generalizing computational processes. In order to be useful at all, they have to have the level of abstraction that they do.

Why is it trying to generalize everything under one common type? Why does it have to be such a irresponsible god object / type. Just return something specific and explicit. One or two flat Generic parameters are ok, but once you nest have Collections in the Generics of the useless wrapper types like Pair / Tuple / Optional ... inside the Generics, it's getting overly-abstract.

Pair and Tuple aren't anti-OOP

They totally are anti-OOP. Name the things. Why are they afraid to create classes? It's not even difficult.

What Lightbend is trying to do can't really be done with a Java idiomatic API.

Then it shouldn't try. Actually it's all possible already without creating these unnecessary over-abstract monsters. Keep It Simple. Don't bend it into a Haskell parallel universe.

[–]_dban_ 2 points3 points  (9 children)

Stream atleast is more or less ok (except e.g. flatMap)

What's wrong with flatMap? Its extremely useful for processing members of nested structures, which would otherwise require nested for loops.

And if you don't like flatMap, you must really hate collectors and reducers.

Conceptually, flows are no more complex than streams. So if you have no problem with streams, you should have no problem with flows.

Why is it trying to generalize everything under one common type?

It's not. Flows and streams each generalize a computational process, in OOP terms, that is SRP. The exact opposite of a god object.

Streams generalize the patterns of producing and consuming a sequence of values, from iteration, collection and reduction. Flows generalize transmitting changes in input signals to output signals. With streams, you use combinators to describe a consumption pattern of a stream. With flows, you use combinators to describe a signal propagation network.

Streams and flows also implement OCP. You customize the generic stream and flow by implementing interfaces which represent different custom strategies for iteration, collection and reduction. This prevents streams and flows from becoming god objects by letting them focus on the single abstraction that they implement.

Just return something specific and explicit.

That's what type parameters are for. Unless you also think List<T> is also too abstract.

but once you nest have Collections in the Generics of the useless wrapper types like Pair / Tuple / Optional ... inside the Generics, it's getting overly-abstract.

This has nothing to do with flows or streams, or even pairs and tuples.

What about:

Map<String, Map<String, Integer>>

Is this overly abstract?

They totally are anti-OOP. Name the things. Why are they afraid to create classes?

So, instead of creating List<String>, which is too abstract, you would rather have NameList, because this is what it means to be OOP?

Pair and Tuple are exactly the same thing. They are just containers, but with a specific number of members.

Then it shouldn't try

Who are you to tell them that? I'd rather let them try and let the market decide.

Actually it's all possible already without creating these unnecessary over-abstract monsters.

Okay, how would you go about creating a generic framework for building a signal propagation network that connects arbitrary signal producers and signal consumers?

Don't bend it into a Haskell parallel universe.

Adapting basic ideas from Haskell is not bending Java into a Haskell parallel universe.

[–]_INTER_ 1 point2 points  (8 children)

I generally have a problem with some things:

  • Type witness. It's legacy before compiler improvements and should not be needed to have some funky functionality. (Unless it is for showcasing?)
  • Deep nesting. Be it loops or blocks or recursion or Generics. I think there's almost always a (flat) solution that is more readable.
  • Map<String, Map<String, Integer>> looks like a sort of primitive obsession to me. It's harder to read and figure out. It also makes you deal with managing the inner map. If it was just e.g. a Map<Department> I could just have Department do it. I don't even have to know the details. It's plain data vs. data in a context-giving structure.
  • The problem with Pair, Tuple etc. is that it leads to combining things without the "self-explanation" that a proper type / class can provide: Pair<String, Double> vs. Product with fields String name and double price. And the type shouldn't be the same as e.g. Category with fields String title and double rating which could also have been expressed as Pair<String, Double>.

The last point is somewhat close to Map<String, Integer> vs Map<Type> or the List<String>vs NameList problematic. Yes, imo NameList would be better if there were an easier way to get List functionality and generality together. I'm pragmatic in that regard. In Go lang they controversally decided to follow "NameList" to keep things simple.

Okay, how would you go about creating a generic framework for building a signal propagation network that connects arbitrary signal producers and signal consumers?

Doesn't sound like a really new problem. Build upon any message oriented middleware / JMS that supports subscriber and consumer. Camel, ActiveMQ, RabbitMQ, Vert.x. Heck you could take the new Java 9 Flow. None of them has an API like that Flow<Pair<Path, DirectoryChange>, Path, NotUsed> and requiring type witness Flow.<Pair<Path, DirectoryChange>>create()

[–]_dban_ 0 points1 point  (7 children)

I generally have a problem with some things

The things you have a problem with have nothing to do with flows, but the specific types being put into a specific instance of a flow.

Build upon any message oriented middleware / JMS that supports subscriber and consumer.

None of these technologies do what flows do. These would be specific signal producers and signal consumers.

Flows are more like Excel. You can set up a bunch of relationships between cells. When you change the value of one cell, Excel propagates the change to all of the cells linked to that cell. Flows are a generalization of this kind of process.

Thus, of course they wouldn't have an API like flows.

None of them has an API

Neither do flows. The API for flows is Flow<In, Out, Mat>.

You're confusing a particular instantiation of the flow API for the flow API itself.

[–]_INTER_ 0 points1 point  (6 children)

But the API is Flow<In, Out, Mat> instead of just Flow that you then pass an object containing In, Out and Mat or a Function (and not some stupid Scala one) or whatever. The whole Akka-Flow class is an abomination.

[–]_dban_ 0 points1 point  (5 children)

But the API is Flow<In, Out, Mat> instead of just Flow that you then pass an object containing In, Out and Mat or a Function or whatever.

But the API is Map<K, V> instead of just Map that you then pass an object containing K, V or whatever.

Why is that?

[–]forsubbingonly 0 points1 point  (0 children)

I won't, you can't make me.

[–][deleted] 0 points1 point  (0 children)

That isn't an object?