tell me that doesn't make you want to run screaming
It may not be great but I prefer this to a higher-order function in a dynamically typed language, where your only recourse may be to read the source code in depth to figure out valid arguments. I have run into this with some Scheme and JS libraries - better hope for good documentation! I really like Scala's type system.
In my brief foray with Scala a couple years back, the thing I remember being most frustrated by was that idiomatic Scala looks too much like idiomatic Perl in some cases. Sometimes blocks would be denoted by parens, sometimes by braces or nothing at all, and it was often hard to tell if you were inside a nested function or not. The underscore was also an unfortunate addition to the language. When I stepped away from it for a couple months and came back, the Scala I'd worked so hard to write "idiomatically" was no longer legible to me.
The great thing about the Haskell signature is that if you understand functional programming at all, it's fairly easy to understand, at least in my opinion. Scala's method signature requires knowledge of Scala's brand of functional programming, including mixins and implicits.
This whole thing is a bit disingenuous, because the type signature argued over is an alternate signature which no one uses, because it provides custom concatenation of Traversables. All of the provided collections have reasonable implementations of concatenation, so this implies you're inventing your own collection.
Everyone uses the alternate form: flatMap [B] (f: (A) ⇒ Traversable[B]): CC[B]. Nevertheless, I'll attempt to explain the provided signature.
If you understand Java generics, you should hopefully be able to understand this response.
A, B, and That are type parameters/generics. So they can be filled in by any type. This flatMap method exists on a trait (think Java Interface) called Traversable[A]. Traversable is similar to Iterable in java.
(f: (A) ⇒ Traversable[B]) means that this function takes one argument called f. That argument is a closure or anonymous function. The closure itself takes one argument of type A (an element in the parent Traversable[A]), and returns a Traversable[B], where B is an arbitrary type.
Skipping ahead, flatMap returns an instance of That. But what's a That? It appears to be unspecified. This is where the "(implicit bf: CanBuildFrom[List[A], B, That])" makes it's appearance.
Implicit parameters are like default arguments. "def foo(i: Int = 12)" will use the i if provided, else it will default to 12. "def foo(implicit i: Int)" will take the i given, else it will grab the first available Int in the containing scope. If you, "val c = 11; def foo(implicit i: Int) = i * 2", calling foo(1) yields 2, but calling foo() yields 22.
The object bf is an object that provides an overridable custom definition of concatenation. So when you write your custom collection, you include an instance of CanBuildFrom[List[A], B, That] in the correct scope, and it gets used for the concatenation. CanBuildFrom's type parameters say that it exists in a List[A], takes a B, and returns a That.
So yeah, in your implementation of bf, you could make it a new MyCanBuildFrom[List[A], B, FunkyCollection[B]], and do funky concatenation.
I don't know, what does a guy have to do to get a language accepted these days. Integrate it with one of the most popular platforms, base the syntax on the most popular syntax, make the type system sound and expressive at the same time... and then it's too complicated. Or too hard for the average developer. Really? It seems to me that scala can be seen as an extended version of java. Though it extends pretty far, sure.
I've used scala on and off for 5 years or so. It seems to me to be a very practical and flexible language. There are things that I don't like, but compared to the number of things I don't like about every other language I use, scala does ok.
And I kinda wonder about the too hard for the average developer thing anyway. Didn't the average developer use to program C++? Despite efforts to the contrary, I still program C++, and compared to that, scala's a stroll on a spring day.
The average developer has changed since a lot of developers coded in C++. Most developers now are what would have been called scripters then. That's not to say there is anything less effective about being a scripter just that their tolerance for languages with a lot of learning overhead is low.
David is spot on when it comes to the complexity of the documentation; it's great that it's exhaustive, but it would be nice to have the "here's how to use it" documentation near the "here's how to implement your own collection type" doc.
Another trend I see in Scala is the abuse of symbols-as-method names. I see a lot of Scala (and Lift) code that looks like
Scala is one of those languages where you need to employ some restraint in order to use it effectively. I think Lift in particular suffers from too enthusiastic a use of the more esoteric language features.
I just think the patterns for what makes a good library interface haven't completely coalesced yet. One can introduce horrifically complex interfaces in Ruby as well, but the best libraries expose a simple, consistent interface.
I hope that's an exaggeration. I'm just starting to look into scala as a directional language for our team (new projects), but if real-life scala code tends toward that sort of ugliness we'll stay with java.
Well, when I say "tends toward" what I mean is if I have an average team of programmers, when I look through the code a year after the project starts, is this what I'm going to find?
I realize expert programmers can write legible code in any language, in the same way Tiger Woods can best me over 18 holes using only a five iron. But there are some languages, like C++, that tempt mediocre programmers into writing illegible code. That's a big strike against, IMO.
I doubt you'll have to worry about that specific problem with a team of average programmers. They won't be naming new functions with pure symbols, which is what that code represents. Just don't tell them Scala allows that and you should be fine. If they figure it out, outlaw it and require the use traditional descriptive English camelcase.
It does whatever you want it to do. ~->, <~<, and %% are just method names. It parses like:
val foo = bar.~->(45).<~<("Fred").%%(x)
If those same methods were called a, b, and c instead, it would parse like:
val foo = bar.a(45).b("Fred").c(x)
Yes, some people abuse symbolic method names. Unless your method makes overwhelming sense with a symbolic name (+, -, *, etc), just don't do that. But this problem isn't unique to Scala; poor names can strike in any language. If I called my method "slfjowierlksdfjnas", that'd be bad too. But most developers have enough common sense to choose reasonable names for their methods.
I've tried to like Scala and it's just hard to do so. Once you've been spoiled by Ruby, Haskell, Python, Coffeescript, Clojure, etc, it's hard to see Scala as more than an obfuscated Java [and, yes, I've been coding for nearly 30 years, so this isn't new-language-cock-fight fanboism]. "case classes"? Really? I read Programming Scala and thought that we had learned better.
I'll take 3 languages that try to make my life easier rather than one language that tries to encompass every possible paradigm... Or perhaps Scala could have stepped the fuck up, worn the hair shirt for a while and done something interesting. By which I mean: see Mirah (http://www.mirah.org/) which is imperfect, beautiful and headed in an awesome, productive direction.
And I realize that I'm not in the sweetspot for Java.next, but I am certainly involved in languages du jour and Scala, even if it is Java.next, is not one of them. Some may point at C and C++ as an analogous set of languages and transitions, but I'd point out that we currently live in a world in which new languages are popping up constantly, are being marketed well and are being tried by large groups of coders. Merely extending an existing language is no longer a recipe for success.
I really disliked Scala at first as well. It is a very complex language, partly due to the fancy type system, implicits, and syntactic sugar. Still, after a year of writing Scala code I've really come to like it. The complexity certainly comes with benefits, and I wouldn't call it "obfuscated" in the least.
That fancy type system goes a long way towards helping to verify, at compile time, that your program is correct. Plus, with dynamically-typed languages you often have to build your own halfassed adhoc type system to check function parameters, map objects to db tables, (de)serialize JSON data, etc. Much better to use a well-thought out one built into the language, that also finds errors before the code is running!
Here's a little real-world example of using Scala types to define the response from a Foursquare JSON API call:
case class Response[T](meta: Meta, response: T)
case class Meta(code: Int)
case class MayorshipsResponse(mayorships: Mayorships)
case class Mayorships(count: Int, items: List[Mayorship])
case class Mayorship(venue: Venue)
Isn't the verification and documentation value of that so much better than using the typical dynamic language's map-of-maps and hoping you didn't mistype a string key name somewhere?
Implicits provide a really nice way to extend the functionality of existing types. Instead of monkey patching, which pollutes your entire program, you can simply write a wrapper class that provides new functionality and an implicit conversion. This is how Scala can give raw Java Arrays the same rich set of methods that any native Scala collection has, and how the standard "map" method can be defined generically but still build & return the expected result collection type.
Syntactic sugar is arguably a problem, there are definitely a lot of special cases to learn and remember: when can parenthesis be omitted, when can curly braces replace parenthesis, special methods with symbol names like "::", ":+", "@", etc. But in the long run I think the sugar does make code easier to read.
Compare a single "map" function in Erlang (a language that really eschews syntactic sugar):
lists:map(fun(N) -> N + 1 end, [1, 2, 3]).
vs Scala:
List(1, 2, 3).map(_ + 1)
which is really just syntactic sugar for:
List(1, 2, 3).map({(n: Int) => n + 1})
I think that in the long run I'd rather memorize some rules (as long as they're reasonable and logical) rather than have to read/write through a ton of boilerplate every time I declare a closure.
I think his argument applies to most languages that are more powerful than what you see in the mainstream. You have to be in the 95 percentile of programmers to be able to use Common Lisp or Clojure correctly, or even to use some of C#'s newer and more advanced language features. In fact, for languages even more steeped in the academic world, like Racket or Haskell, I would pin that percentile more at 99.9.
Languages like Gosu and Kotlin are simpler, but just aren't bold enough to make a big difference in overall productivity. My personal hope is that some future generation of programmers is just better educated. I don't know how else any of these languages or tools will ever gain widespread acceptance.
I'm no Kotlin expert, but it looks like C# and that's what I do at work. I've been playing with Scala at home and the fun-factor in Scala is several times that of C#.
Jetbrains should post a Kotlin vs C# to clear up what's really the difference between the two languages. If the difference is really small, are they trying to take advantage of Resharper to introduce a new language on the JVM?
I like Scala and don't find it that difficult, but I grudgingly came to the same conclusions as the OP. It's just a bridge too far for any kind of mainstream acceptance. I think a less ambitious project like Kotlin could still do a lot to improve the lot of the Java programmer without incurring the extra complexity of Scala.
He tries to make a distinction between library-level and consumer-level in regards to the type-system and the user docs; hmm, where have I heard that before?
And how can you make that distinction in a language anyway? Aren't users supposed to implement their own flatMap -like functionality if not available?
I tend to agree. One of the things that won me over to the idea of using an IDE for Java is that I can (effortlessly) click into a class from a library (or the JVM itself) and read the source code. Being able to read the source so easily has rendered Javadoc superfluous for me. I'd hate to think that Scala source code would become so "hard" that it's no longer useful as documentation.
Interesting and honest article from a big Scala promoter.
I have been thinking about this in regards to Haskell- and much of this article would apply. But you can't write Java instead of FP in Haskell. Some aspects of Haskell are harder to learn, and some easier. I feel the biggest issue for a user of a library is: can they understand the compiler error messages? This places constraints on the level of abstraction that a library writer can achieve if sharing the library with the community.
It sounds like Scala is hard because it's got too many paradigms and inconsistencies, and Haskell is hard because it's got very few paradigms and is very consistent. (That's not sarcastic; when a language has many ways to accomplish a thing it is easier to find at least one of them.)
I think we have to distinguish between hard to learn and hard to bring and integrate into a team.
Both are hard to learn largely due to the choice of Strong typing, which is fairly independent of paradigms. Haskell is hard because it adds the purity pardigm, although you could say that is just a full embrace of FP.
Haskell is hard to bring into a team because it is pure FP, whereas Scala has the Java/OO backdoor. But this article points out that it is not easy to get every one to write and maintain good Scala in a team for that same reason. If a team attempted Haskell they would have a true pass/fail test and be less likely to end up in limbo.
Maybe that is Haskell's best point: It is hard to write bad Haskell. I have seen it- code littered with IO, poorly named variables/functions, and passing large amounts of arguments and configuration state. But that is very rare and re-factoring even that is usually at least very safe and sometimes straightforward. So it is difficult for a novice to do a lot of damage. But instead of intentionally limiting power to prevent a programmer from doing things, Haskell gives you a great deal of power- just in a way that greatly prevents it from being abused- the novice can eventually harness it.
Yeah, if your talking about eclipse, I'm a bit surprised myself also. The only big thing I can think of that it is lacking is type searching. Maybe quick hints too but thats not that big of a deal. Other than that it gives autocompletion, import management, inlining/extracting functions, etc. Is there something I'm missing?
What do you mean by type searching? Ctrl-shift-G finds all references to the currently selected symbol. Ctrl-shift-T gives you a quick find/open on the symbol name you type.
Eclipse is really fantastic. For an excellent demo of how to use it effectively, see Notch's (from Mojang/Minecraft) participation in Ludum Dare this year.
I think he may mean searching by type, not searching for the type of something. Hoogle does this for Haskell and it's a fantastic tool. Let's say I want to find if an element is in a list but I don't know the name of the function to do that. It should have a signature like [a]->a->Bool (take a list of a's and one a, and return true or false). Well, here it is:
Eclipse is pretty nice, but none of that is particularly unique to it anymore (and it often does feel just a little clunky - I've started moving to IntelliJ mostly for that reason).
It does get a bad rap that's not really well-deserved, though.
- implicits: "-Xlog-implicits" switch tell you where they come from (and the IDEA plugin works pretty well on this issue), and explicit return types for implicits:
But it's not tho', well maybe it is if you're used to Java, but coming from ML you will boggle that it can't infer types in function arguments.
Also Zed Shaw, WTF has he got to do with Scala? I think you will find most people who are more into code than they are into flamewars/trainwrecks won't have heard of him, and why should they?
I studied it with a couple of friends at work and I ended up with same conclusion. Scala was hard. Way too hard for the average corporate programmer. It could work in a startup environment, it would not work in corporate america. Flat out, the programmers are just not there.
This is why Gosu has, for example, covariant generics. Yes, it is unsound. But it is simple and good enough.
Type systems should be there to help us write code, in particular to support things like IDEs. Correctness is great as far as it goes, but it turns out it doesn't go too far and you get 90% of the correctness benefit with, hey, look at that, 10% of the effort and complexity.
But seriously, what is the point of having a typesystem which is not sound? Nobody is going to mind if you create another dynamic language ala Python, but design a type system (and force your users to use it) if it doesn't provide the protection against fucking up the types?
Static type systems (particularly explicit rather than inferred ones) have multiple goals: as a form of documentation; as a way of specifying interfaces so pieces can be more reliably developed independently because the seam isn't just in documentation or peoples' heads or a bunch of unit tests; as a way of designing an app (when program = data + algorithms, some people like to start with the shape of the data); and finally as a way of proving more things about the code.
I'd submit that the historical actual use of types doesn't emphasize the proof nature of types nearly as much as academic CS does. Unsound type systems are common in practical languages. And I believe that isn't accidental: because, with all the other things types are used for, you need an expressive type system, and that in turn means it is either going to be complex and sound, or simple and unsound (i.e. with escape valves like typecasting and runtime type errors - I'm considering compile-time type analysis only).
"what is the point of having a typesystem which is not sound?"
Because it gives plenty of benefits even when it's not "sound". What your suggesting is that there should never be any language between dynamic languages and haskell. Clearly there are many people who find type systems highly useful even if they don't go as far as haskell.
He just told you; to assist the IDE, things like automated refactoring, something Ruby still doesn't have to any reasonable degree. Type systems don't have to be perfect to be damn useful, if you like type systems.
Your developers already use (the advanced features (linq)) of C# : Easy(ier). C# is dragging a lot of my fellow developers to the functional world.
It's fascinating to watch as these developers "get it" finally.
I found Scala very close to functional C#. In fact I'm most comfortable now with Scala on the JVM (when i must be there) as Java itself has stagnated for so long, it's no longer fair to call C# a Java clone.
#77777 for text colour on a white background? Yes, David, usability is hard!
I actually had to Ctrl+A the whole thing to read it, and I'm sitting in front of an IPS LCD with real 16MM colours. I wonder what do folks on an average-terrible PC laptop screen will see?
"Your developers come in at 9:15 and leave before 6 and don't check work email at night: Hard"
Am I the only person who find this attitude obnoxious? I know -- and I'd like to include myself -- some good devs who don't sell 24x7 to our employers.
edit: in particular, I don't check work email at night. I don't do ops. People should write better code or hire a different engineer.
Some of the most productive times I've ever had writing code was when I arrived at work at 10, and left at 5:30. I didn't really get started until about 11, and usually had an unproductive period around 1 till 3 as I ate and digested lunch. But the work I did do in those ~4 hours was some of the stuff I'm most proud of ever doing. I still got some looks leaving in the evening from the managing director, but the way I saw it, it was pointless continuing when I was tired - I always got better ideas the next day anyway after sleeping on the current problems.
I came here to comment on the same thing. Even if I happen to check work email some night or weekend that I'm more bored than usual, I make a point to not reply.
Oh, and this was similarly obnoxious (if not said tongue-in-cheek): Your developers stare blankly or say 3 "Hail Marys" when they hear the name "Zed Shaw": Scala == Hard
Dude, seriously? Since when are programmers judged by their propensity to keep up with the latest gossip about the most loud-mouthed self-important subculture celebrities ?
> Oh, and this was similarly obnoxious (if not said tongue-in-cheek): Your developers stare blankly or say 3 "Hail Marys" when they hear the name "Zed Shaw": Scala == Hard
That basically means if you follow technical news, and cutting edge stuff, you have most likely heard of him - for his software(mongrel2, lamson), his books, his essays(rails is a ghetto, programming motherfuckers). Mongrel was a big deal some time back, and still is in the rails world, and rails is a big deal; all these factors kinda make Zed a known name.
If you don't know Zed, that's not a big deal - no one gives a fuck about Zed. But not knowing Zed here is weakly implying(not necessarily though) you have been living under a rock and you aren't familiar with Rails, Mongrel2 etc, which is not a good thing IMO.
Cutting edge means "latest or advanced stage of development". Rails was miles ahead of existing frameworks when it first came into scene. Rails was cutting edge, and it needed a stable application server - mongrel filled that niche.
And a small niche in the computing world can very well be cutting edge.
Please. Zed's fame (or rather notoriety) doesn't come from Mongrel (never heard of lamson) and afaik his main association with Rails is the famous rant. Can you name off the top of your head some of the core developers of FreeBSD, PostgreSQL, JBoss, Hadoop, Gnome and a dozen others more important FOSS projects? I can't, and yet this doesn't say anything about both the significance of said projects and my familiarity with them.
Significance - no. Familiarity - I'd say yes. Unfortunately I don't know the projects you mentioned that well, so I can't say anything about them. But from the projects I do deal with (either developers or people related to project in some way):
- Python: van Rossum, Beazley, ...
- Mono: de Icaza
- Mysql: Monty Widenius
- Ruby: Katz, Shaw, Bini, ...
- Chef: Matt Ray
...
Yes - I do believe that if you claim serious knowledge about some project, but can't name anyone involved into that project in one way or another, there's something weird going on. Typically you'd run into some reoccurring name while looking through bugs, mailing lists, manuals, etc.
> I do believe that if you claim serious knowledge about some project, but can't name anyone involved into that project in one way or another, there's something weird going on.
Even if this is true (which is debatable at best), it's a long stretch from the blogger's aphorism "If you don't know $VOCAL_DIVA_PROGRAMMER, Scala==hard". He obviously doesn't imply you should be familiar with Mongrel to have a chance to become good in Scala.
All this nonsense would be unnecessary if the author made his point by mentioning, say, "referential transparency" or "map-reduce" instead of "Zed Shaw".
I agree with your opinion this part of the article is silly/wrong. Then again, I was responding directly to the parent's claim, rather than "commending on the article through responding to the parent" ;)
> Can you name off the top of your head some of the core developers of FreeBSD, PostgreSQL, JBoss, Hadoop, Gnome and a dozen others more important FOSS projects? I can't, and yet this doesn't say anything about both the significance of said projects and my familiarity with them.
It's more like there is an important project, and there is a loud mouth, and you use that project - you have most likely heard of the loudmouth. Linus Torvalds, Richard Stallman etc. are known because the projects they work on are important and we use them, and they are vocal, and sometimes arrogant about their projects.
Though Zed isn't in the same league, the same goes for him. He was associated with an important project, and he created controversy while he was associated with that project. You can write articles on "Rails is a ghetto" or "Django is a ghetto" or "Java sucks", and you won't be getting any coverage unless you already are a somewhat known name in the community. Zed's notoriety doesn't directly stem from the essay.
> It's more like there is an important project, and there is a loud mouth, and you use that project - you have most likely heard of the loudmouth.
True, but the point is many/most people have heard of Zed without knowing or ever using his project(s). On the other hand the number of people who know Linus as the creator of Linux is orders of magnitude larger than those who also happen to know he's sometimes arrogant.
No need to rubbish Zed. You can like Zed like I do, and still not make any conclusion from the fact that a developer doesn't know who he is. Except, maybe, that said developer doesn't read Hacker News either.
I agree. If he means developers who only spent time on programming related things during the 9-5 and not during their free time, maybe I can understand. Good developers should be passionate and will probably spend their free time learning and coding (not necessarily work related) but to say the only good developers are ones that are slaves to their job is ridiculous to me.
It's the trendy thing to do these days. You can especially see it on HN there is a lot of bashing of programmers that don't code outside of work and/or only work 40 hours per week.
Programming is a creative endeavour. All these accomplished and renowned 9-5 programmers must hang out at the end of the rainbow with all the accomplished 9-5 painters, musicians, etc. I'm not saying there aren't any - some people really just have a knack for it. I'm saying they are the exception and not the rule.
At work you're often focused on completing tasks on time. Good employers understand that you need to develop skills and will allow you to do so on the clock. Many people are not fortunate enough to work at such places and if they don't sharpen their skills outside of work then their skills dull with time. Novices that work at such places will have a very hard time progressing in such an environment if they don't take the initiative in their spare time.
Working with developers that are more skilled than you are is also a good way to learn and improve. If you don't get that at work then you better find or start an OSS project or something to contribute to.
Sorry for replying to myself. I want to mention that not everyone needs the Sistine Chapel, that is there's a lot of room for all skill levels. The guy closer to the other end of the spectrum who knows just enough PHP to hack something together still has a skill that others, who perhaps do not need a Michelangelo, are happy to pay him for. There's nothing wrong with that.
When most of us use the word "good" in this context we mean significantly better than average. I think it comes across as an implication that if one isn't a "good" dev then they're bad but that's not what I intend at all. In fact what many of us mean by "good" is probably closer to "best".
That's odd, since “overtime is bad, more time is not more productivity” seems to be all the mantra.
The thing is, that for a young, smart person, mechanically giving up 50, 60, heck, 100 hours per week is not that hard, but it gives the nice “hero” feeling, social kudos.
Empirically, in full-time scenario (30+ hours per week), there is no difference in velocity of development work with more time spent; yet, there are toxic social effects.
Basically: what do the people who bash “undertimers” think?
Exactly what I was going to point out also. Maybe the OP was throwing in some humor? The only reason I say this is that it seems like all other criteria are also not very concrete. For eg, following so and so on twitter, lunch-time discussions - as opposed to having a good understanding of type systems, functional programming etc.
Being an extremely effective professional programmer (as opposed to a competent one) requires you to learn and do some things that you won't learn in school, and also that no sensible person would pay you for. This is just fact and people who don't like it either may accept being just competent or are in the wrong career.
What does that have to do with regularly burning the midnight oil for no tangible reason? I study computer science and new technologies when off the job, but you won't catch me dead working on company code after hours.
I can understand expecting a good dev to be interested in programming outside of work but expecting someone to give some company hours for free? Sounds like the guy is clueless. It's hard to imagine anything else he says will be useful given that sentence.
People who aren't used to Scala complain that it's ugly, but I find that language beauty has a lot to do with familiarity. Ocaml and Haskell seemed ugly (to me) at one time, and now I love those languages. Lisp and its parentheses? You get used to it after 2 weeks, but some people hate it at first. The sign of an aesthetically defective language is that it stays ugly even after you get a hang of it.
Scala may be The Compromise That We Have To Make. In the functional programming (i.e. good languages) community, we're so passionate about languages that we become factional, divided between Ocaml and Haskell and Clojure and Erlang and F#. What comes out of this is that we divide ourselves up into 1-2% silos and end up having to use Java, because people who don't care about PL got behind a small set of languages a long time ago.
I don't know Scala very well and may end up eating my words here, but I think it (or F#) may be the language we need to Get Behind, at least for now.
Scala is statically typed and can take advantage of the JVM libraries. It actually has a shot at becoming a major production language in professional software.
Also, what strong, static typing does to bad developers (spits them out brutally) is an asset to the software world. It makes sense for talented engineers, it accelerates the learning process of the talented-but-inexperienced ones, and it fires the incapable ones so you don't have to.
...
tell me that doesn't make you want to run screaming
It may not be great but I prefer this to a higher-order function in a dynamically typed language, where your only recourse may be to read the source code in depth to figure out valid arguments. I have run into this with some Scheme and JS libraries - better hope for good documentation! I really like Scala's type system.
In my brief foray with Scala a couple years back, the thing I remember being most frustrated by was that idiomatic Scala looks too much like idiomatic Perl in some cases. Sometimes blocks would be denoted by parens, sometimes by braces or nothing at all, and it was often hard to tell if you were inside a nested function or not. The underscore was also an unfortunate addition to the language. When I stepped away from it for a couple months and came back, the Scala I'd worked so hard to write "idiomatically" was no longer legible to me.