- Feature Articles
- CodeSOD
- Error'd
- Forums
-
Other Articles
- Random Article
- Other Series
- Alex's Soapbox
- Announcements
- Best of…
- Best of Email
- Best of the Sidebar
- Bring Your Own Code
- Coded Smorgasbord
- Mandatory Fun Day
- Off Topic
- Representative Line
- News Roundup
- Editor's Soapbox
- Software on the Rocks
- Souvenir Potpourri
- Sponsor Post
- Tales from the Interview
- The Daily WTF: Live
- Virtudyne
Admin
Introducing the term Enumerable for streams (by want of a better word), seems a lot more bizarre. Especially considering that the term in functional programming has always been... streams (well at least since the 1980s), and enum (short for enumerable) is something totally different and has been in use since forever.
Admin
I never can get over the horrible syntax Java has developed as a C# developer. Can someone help me here out, what this actually does?
Admin
Best I can tell it's the equivalent of using LINQ to create a subset of a larger array and using that as the collection for foreach. Nothing much spectacular or WTF. It's just that the use of ternaries, indentation and the general execution of the idea is done very ugly.
Admin
I'm not sure what functional languages you are referring to, but both Java and C# are C- based procedural languages (with some functional elements). In C and C++, stream means file stream or socket stream - low level byte streams.
Then, we look at the dictionary: https://www.wordnik.com/words/stream
A steady flow of data.
To transmit (audio or video content), especially over the Internet, in small, sequential packets that permit the content to be played continuously as it is being received and without saving it to a hard disk.
Both noun and verb definitions unquestionably refer to byte streams. So....
Admin
I do agree though that enums and enumerables can be confusing. However, given the pool of words to choose from, I don't think there's another easy to use word available for enums, or enumerable collections.
Admin
MaxiTB - the problem isn't Java syntax, except to the extent that the Java compiler allows this kind of thing to compile. The problem here is that this looks like someone who doesn't understand the tools they were given cobbling together something without realizing that what they've done is bad.
As for what it does - basically it's a roundabout way to sort a copy of the imagesAndVideos collection into a List and sorting it, and then interating over the sorted list with a for loop. The (Entity e1, Entity e2) -> ... syntax is a Java anonymous function declaration to provide a Comparator for the sort to give the order, and the implementation is basically that e1 and e2 have a field called "sortorder" in them that is an integer value or possibly is null and so the ternaries are there to make null values into 0s (which possibly points to a higher level design issue, but even if not if you're using Java's functional syntax you should be Optional to deal with nulls not rolling your own version with ternaries). The final collect() call at the end collects it all into a list.
Mixing Java's functional syntax with Java's imperative syntax to create code that displays some of the worst aspects of both. It really does feel like someone who was playing with Java 8 back in 2015 and discovered something that they COULD do but didn't realize that they SHOULDN'T do it.
Admin
Sort of. Calling the Java thing a "stream" could have "stream of separate objects" as its origin - I don't think there's any obligation for them to be pre-created before we, um, enumerate through them. I certainly can see uses for a streamable object that generates a new streamed object each time you ask it for a new thing.
On that basis, it's arguably a better name than Enumerable, which implies (from a name-semantics point of view) a group of pre-existing objects, even if they are actually generated on-the-fly.
All of which comes back to my general assertion that naming things is actually one of the hardest parts of developing software.
Admin
There's too much of that in the wild.
Admin
Actually, Java now has the iterator approach, which replaced their legacy enumerator approach. While iteration is not necessarily the same as enumeration, because you can iterate 10 times arbitrarily, perhaps it's a name the industry can move to in the future? Then we'll have enums (hard coded list of options), iterators (foreach), and streams (byte level IO).
The problem with "stream" for iterators/ enumerators is that it's already widely known to mean byte streams, both by end users and most C- style language programmers. And there isn't enough of an advantage meaning wise to uproot this meaning. Literally everybody says stream for bytes and AFAIK only Java says streams for pipes iterators. Weird
Admin
In defense of the developer, here is the "best" functional version of the code I could write in java 8:
imagesAndVideos.stream() .sorted(Comparator.comparing(e -> Optional.ofNullable(e.getValue("Sortorder")).orElse(0))) .forEach(media -> …);
Still ugly when compared with fresher languages like kotlin:
imagesAndVideos .sortedBy { it.getValue("Sortorder") ?: 0 } .forEach { media -> … }
Admin
If you take the terniaries out what's left isn't horrid. It definitely represents an awkward mishmash of functional vs iterative style. But for many of us functional style IS a bolt-on to our longstanding iterative mindset. A bolt-on we speak, but not fluently, if you get the joke.
We certainly see in any Java or .Net codebase of any age lots of code written before functional style became possible, much less commonplace. Into which bits of functional style have been injected here and there as new features, bug fixes, etc.
Now as to those terniaries ... That totally smells to me like a bugfix. Or rather an afterthought. As in the original dev was surprised during live data testing by unexpected nulls, so had to shoehorn the null fixups into their "beautiful" functional chain. And again being somebody who still thinks in iterative impertive terms, and only haltingly translates that thinking into functional style, they fixed it with imperitive terniaries versus the more fluent
Optional()
None of which makes the code "good". But does make it unsurprising, at least to me. The careful indentation seems like the work of somebody who was at least trying to be neat, even if the total result is like my Spanish: right words, some wrong conjugations/affixes, and uniformly terrible accent.
Admin
Well, no. No to either of Remy's objections. Here's the same thing, translated from Java streams into C# Linq:
Hmmm.
Now, for pedagogic purposes, I've simplified the comparator, but that seems fine to me.
Leaving aside Remy's mild objection to a comparator being identified by a repeated string (annoying, but what the hell are Jitters for?), this is not actually a WTF. If it was a WTF, I wouldn't be able to turn it into Linq quite as easily. And note that the ternary isn't really all that objectionable. (Although in Linq I would obviously filter out nulls first, so, y'know, a 5% improvement in common sense, there.)
The reason the OP looks bloody horrible is because almost anything in Java Streams is going to look bloody horrible. Java Streams is a Write-Only language, a la Perl.
But (and ignoring the implicit equivalence between the collect in the original and my ForEach, and pretending that ForEach is a good thing) is it a WTF?
Not really.
Admin
It's the second hardest problem in computer science.
Admin
"Write-only language" - I like it!
Admin
So it's basically what we write like this in C# ?
var entities = imagesAndVideos.OrderBy(e => e.Sortorder ?? 0).ToList();
Admin
Oh wait, my mistake, I thought SortOrder is a property getter - it's a bit confusing in Java.
So basically it is:
var entities = imagesAndVideos.OrderBy(e => e.GetValue("Sortorder") ?? 0);
foreach(var entity in entities) { // Stuff }
The ToList makes no sense in C#, it would just waste memory. foreach is generally preferred over ForEach, especially when you are doing mutations.
Admin
This isn't a Stream WTF, it's a Comparator WTF. Sure, .stream().sorted().collect() is a verbose way to sort stuff, but it's not terrible. The comparator looks like crap, though, and should definitely be replaced with Comparator.comparing().
However, TRWTF is Entity, which appears to just be a kind of (String, Object) map. Any code that has to interact with this is going to be ugly due to the casting and null checks. If we just had this:
interface ImageOrVideo { int getSortOrder(); }
we could write beautiful code:
imagesAndVideos.stream() .sorted(Comparator.comparing(ImageOrVideo::getSortOrder).reversed()) .forEach(media -> /* ... */);
Admin
(that last line was intended to have some line breaks, it's a bit less beautiful now)
Admin
Maxi.
That's a much better translation than that of 'Sole Purpose Of Visit'.
You want to use 'ToList()' in case that it is likely that 'imagesAndVideos.stream()' will change when were iterating over the sorted list. Think of Windows Explorer sorting the results of a search that is still running.
C# will throw if the collection's changed during iteration.
Admin
Java Apologist.
In your example performance is lost when you first sort the list and then reverse it. Better to use a comparer that can return the media in reversed order.
Admin
It wasn't meant to be a "best of breed" translation. Just one that shows equivalent thinking to the OP.
And no, I wouldn't do it that way. I wouldn't use ToList() for a start, and I wouldn't use ForEach(). I might even spend more than five minutes writing the first thing that came into my head.
However, if I did do it that way, I'd regard it as "messy but intelligible." Not really a WTF.
Admin
I haven't used an actual functional programming language since university, and even then it was a (home-brewed) research project.
However, there are references for the term "stream" in functional programming: "The first reference to streams in computing was by Peter Landin in his 1965 paper “Correspondence between ALGOL 60 and Church's Lambda-notation”. Functional programming was his chief interest and the paper certainly describes the concept in those terms." Unfortunately, I have no link to the paper mentioned.
Maybe this clarifies it a bit: https://sites.ualberta.ca/~jhoover/325/CourseNotes/section/Streams.htm
Streams in java are basically referring to datasets. Datasets are "streamed" through a set of operations. Because the intermediate (and final) datasets are often not the same type of dataset as the original, the term enumerable seems to be just wrong to me. But not being very familiar with C#, I may misinterpret its meaning. An I/O stream can be considered a very simple dataset in this context, consisting of bytes, bits, characters or whatever..
Admin
I haven't used an actual functional programming language since university, and even then it was a (home-brewed) research project.
However, there are references for the term "stream" in functional programming: "The first reference to streams in computing was by Peter Landin in his 1965 paper “Correspondence between ALGOL 60 and Church's Lambda-notation”. Functional programming was his chief interest and the paper certainly describes the concept in those terms." Unfortunately, I have no link to the paper mentioned.
Maybe this clarifies it a bit: https://sites.ualberta.ca/~jhoover/325/CourseNotes/section/Streams.htm
Streams in java are basically referring to datasets. Datasets are "streamed" through a set of operations. Because the intermediate (and final) datasets are often not the same type of dataset as the original, the term enumerable seems to be just wrong to me. But not being very familiar with C#, I may misinterpret its meaning. An I/O stream can be considered a very simple dataset in this context, consisting of bytes, bits, characters or whatever..
Addendum 2022-02-04 06:59: What, the double postings are back?
Admin
Idiots
Admin
Ah I see.
In C# (and .net in general) all methods are considered not thread-safe unless they are declared like that. So you would actually turn the result into an array BEFORE you iterated over it using ToArray() in a concurrency safe way. In other words, C# place the burden on the caller to ensure thread safety, because depending on the caller context, there is different ways to achieve this (that's the only secure way to remove the risk of deadlocks).
To be extra safe you can always fall back to SemaphoreSlim's to avoid your code being deadlocked in an async context, which would look like that:
_lock.Wait(); try { var entities = imagesAndVideos.ToArray(); } finally { _lock.Release(); }
var sortedEntities = entities.OrderBy(e => e.GetValue("Sortorder") ?? 0);
foreach(var entity in sortedEntities) { /* Stuff */ }
Admin
As opposed to Windows calling file systems "drives", character sets "code pages", 16-bit integers "words" etc.?
Admin
You cited a 1960s paper talking about ALGOL and the university website page which uses the word stream to describe iterables. WRT ALGOL, it's hardly relevant today. And that web page offers an opinion, but you know the saying about opinions. My point is that in C++, stream usually refers to byte stream, and more importantly, that's what most laymen understand it as. Believe me, I can see how one could pick the word stream to describe lazy piped iterators (or memory LINQ in .NET parlance), if the world was a blank slate. The problem is that the world isn't a blank slate.
Admin
You missed out Dennis Ritchie's Unix Streams, which were a hideous attempt to side-step the standard comms layering from dixie cup through barbed wire through Z8080s through the OS through user space libraries to the lucky recipient of whatever bit-headed gibberish gushed out.
As far as I am aware, Unix Streams died a deserved death. One can only hope that Java Streams also die, in due course. (Well, Java would have to die. But that is a very small price to pay.)
Admin
Fascinating. And completely irrelevant, since the OP doesn't mention threading or asynchronous programming in any way. But let me try my ninny translation again -- this time without ToList(), which faithfully replicates the OP but is in fact obnoxious in C#, and this time with parallelism:
Seriously.
This stuff is not conceptually difficult. You can complain that dotdotdot is not trivially paralleiizable, in which case use something else like partitions. You can complain that Java doesn't do it this way (the OP never claimed that it did). In which case, use whatever the Java equivalent of TBB is -- almost certainly as butt-ugly as everything else in Java Streams, but not actually a WTF.
The implementation details are not important here. The abstractions, and the way that the abstractions are exposed in Java Streams, and even I suppose the fact that the code offered up by the OP clearly doesn't understand the abstractions ... that's what matters.
Admin
That's a Z8530, btw. Memory betrays me, but sequential memory even more so.
Admin
Java's use of the term Stream comes from other Functional languages such as Haskell and Scala (among others) which use the term to refer to a potentially infinite sequence of some type. Java's stream implementation is more or less a cheap knock-off of what those languages provide. One could argue that the IO/bits-and-bytes style stream of C/C++ (and lisp) is a constrained subset of the more general Stream as described in these other languages (i.e. in general terms, a Stream is an indefinite, potentially asynchronous sequence of values. An IO Stream is a Stream constrained to input/output operations. An Enumerable is M$ inventing a term when a perfectly good term already exists (granted there are likely name conflicts and the use of the general term wasn't as wide-spread as it became by the time Java employed it in Java 8) and declaring themselves a (new - at least at the time) standard (as if there's really anything new there)).
Also, wrt people complaining about Java Appologist's reversing the order after sorting it, didn't pay attention to the order of operations: sorted(Comparator.comparing(ImageOrVideo::getSortOrder).reversed()) Note that .reversed() is called on the comparator, creating another comparator that sorts things in reverse order, so it doesn't employ a second pass to reverse things (which isn't really that big of a deal when you remember that the sort operation is generally more expensive than the reverse operation. Besides that, iirc, calling .reverse() on a java stream doesn't immediately reverse it, only sets a flag somewhere that downstream (terminal) operations may need to pay attention to when accumulating results with the potential for further optimizations).
Admin
When did Java become a functional language?
Admin
Collecting to a list just to iterate over it immediately doesn't make sense in Java either, unless something ghastly is going on with the body of the
for
. (It'd make more sense if it was returning it; that could pass the value to somewhere where the stream itself is unsafe to use, such as out of a transaction context.)Admin
The ugly part is the code to fetch the
Sortorder
; that's basically begging to be factored out into a little (private static
) helper. It points at the underlying data model likely being a WTF but it's just ugly in itself and not a WTF. If we just fix that in the not-thinking-very-hard way then we get this, which is still a WTF:Admin
If you are nitpicking on the phrasing "from other Functional" as if to suggest that this was meaning to include java in the set of of Functional languages, that wasn't my intent with my sloppy use of my first language (I was more so intending to imply that there were other Functional languages which did not employ the stream idea in this fashion, which may have been mentioned or alluded to in this discussion). It wasn't my intent to suggest that java ever became a functional language - it's just as functional now as it ever was (take that however you want :p ).