22 comments

[ 2.6 ms ] story [ 59.9 ms ] thread
> meaning you can build functions like this:

Is it just me, or is that code example incredibly byzantine? I know I'm only a Swift dilettante, but that struck me as the kind of code we want to actively avoid building.

The conceptual grouping, the syntactic grouping and the line layout grouping seem to be carrying three contradictory messages about structure, requiring careful parsing to grok.

Yeah I've used Swift a bit and found it in need of cleaning up.

To be quite honest, I've found in the last 5 years a lot of professional developers have lost sight of the maxim that brevity is not better than clarity.

Do you have any examples you care to share of Swift that needs cleaning up?
Sorry for the poor wording, I was responding to the comment saying that the code example was hard to read. I was agreeing with that. I really like Swift itself.
That's the biggest problem with adoption of academic-style (Haskell, OCaml etc.) functional programming languages.

The proponents believe that 'shorter = more readable'.

And if they would try to make it more readable it would start looking like Scala and C# (hence their popularity).

Don't blame Haskell, OCaml, etc. here. 90% of that syntax that is in the example is Swift specific and I agree it is cluttered and ugly. Swift would do better to stop trying to be like C/C++ and be more like ML (you can tell the language wants to be like ML, but I'm guessing there are internal politics to make it more C/C++-like for adoption reasons. There's even (or there were) a few design documents hinting at intentionally being C like).
I don't know, it seems to me that at least a few in the core swift team are well known C++ hackers. For example, in addition to Chris Lattner of LLVM fame, David Abrahams, which I believe is still the lead in charge of Swift standard library, was a founding member of Boost and, relevant to this post, (co-)author of Boost.Iterator. Doug Gregor which is also part of swift core team, is also a prominent ex-Booster.

Edit: Doug was of course also the principal author of the failed C++11 Concepts proposal

I don't think the OP was "blaming" Haskell and OCaml as much as he was blaming the people trying to add Haskell and features and syntax to Swift and C++.

Haskell syntax works great for Haskell, because the whole language has consistent syntax for things like lazy evaluation and lambda functions. The problem is when people try to tack on those features to Swift or C++ and make it work with the ugly syntax from those languages, it ends up making it even uglier.

To me, the sample code looks like a bastardized mix of C++ and Haskell, and it's pretty hard to figure out what it's doing, even in the toy code samples.

Great point. Thank you for bringing out the distinction in OP's post.
Similarly, there are people who will insist that the most "pythonic" solution to any problem is list comprehensions nested 3 levels deep.

It's OK to wonder "Can I do this one one line?" as long as you can step back and say "Yes but I'm not going to" sometimes.

That was one thing I really liked about the Google Python guideline posted here a few days ago: Their rule about list comprehensions. "three lines max, one for the element, one for the list generation and one for the filter clause - no nesting". That's the sweet spot.
And they're just wrong; that goes directly against the Zen of Python:

  Simple is better than complex.
  Flat is better than nested.
  Sparse is better than dense.
  Readability counts.
I too have a tendency to overuse list comprehensions over regular loops, but when they start nesting I know it's almost always time to break it up.
The problem is that to some degree, shorter is more readable, but only where it allows easy interpretation of intent or allows more state to be visible at a time. Like most good things when taken to extremes, the positives are quickly outweighed by the negatives.

Keeping names informative is a good thing. Keeping names small enough that you don't have to scan across half a page when they are used is a good thing. Keeping thunk definition separate from control structures where it is used, when it's more than a line or two is generally a good thing. Finding the sweet spot when these and many other rules conflict is where experience comes in, and is part of the art of programming.

(comment deleted)
If anything, the problem with the given code snippets is that the code is needlessly cluttered by explicit type annotations for trivial things. Unfolding a lazy sequence is a short and sweet affair in Haskell and even ML (which doesn't enjoy the benefit of piggybacking on built-in lazy evaluation), where you only supply type annotations when inference can't possibly work (e.g., polymorphic recursion, which most code doesn't need).
I'm not very familiar with Swift. If I'm understanding right, the `prefix` function being defined is equivalent to LINQ's `TakeWhile` [0]. If so, the C# version seems much easier to read to me:

    public static IEnumerable<T> TakeWhile<T>(
        this IEnumerable<T> source,
        Func<T, bool> predicate) {

        foreach(T value in source) {
            if (predicate(value))
                yield return value;
            else
                yield break;
        }
    }
Of course, there's a bunch of compiler magic going on here that's basically creating a state machine, which seems to be explicit in the Swift version. Are there any similar features in Swift to C#'s `yield`? If not, are any on the road map?

[0] https://msdn.microsoft.com/en-us/library/bb534804%28v=vs.110...

Beyond being a little more explicit about the generator semantics, it's just noisier. "Self.Iterator.Element" is a lot of noise compared to "T".

At the moment, I feel these sequence extensions are just allowing people to write toy programs with slightly less boilerplate, and are accomplishing little as far as improving our ability to produce useful software.

Unfold in Swift? Seems like a good step. It's a bit annoying to use raw unfold but it's easy enough to build utilities on top of it to capture common cases. Get the primitives right and everything else becomes easy.