Hacker News new | ask | show | jobs
by yoz-y 2472 days ago
To me this looks like perl golfing, but for some reason perl golfing is bad but writing extremely terse functional programs is not.

Personally I like to combine both approaches. In swift I would write something like this:

    let l = ["abc", "ab", "abcdef", "abcdefgh"]
    width = l.reduce(0, { max($0, $1.count) })
    let centered = l.map({
     (line: String) -> String in 
      var padding = (width - line.count) / 2
      return String(repeating:" ", count: padding) + line
    })
5 comments

Obligatory C#:

    var input = new string[] { "abc", "ab", "abcdef", "abcdefgh" };
    var maxLength = input.Select(e => e.Length).Max();
    var output = input.Select(e =>
    {
        var padding = (maxLength - e.Length) / 2;
        return new String(' ', padding) + e;
    });
Most modern `oop` languages these days all support functional constructs and achieve the same thing in same amount of code & style. Language and oop/functional style are not mutually exclusive anymore, which this article seems to overlook by comparing both languages and styles at the same time.
This looks like the Java if the Java was written by someone who knows Java:

    List<String> alignText(List<String> texts) {
      int maxLength = texts.stream().mapToInt(String::length).max().orElse(0);
      return texts.stream().map(text -> {
        var spaceCount = (maxLength - text.length()) / 2;
        return " ".repeat(spaceCount) + text;
      }).collect(Collectors.toList());
    }
/nit Move the lambda to private method and replace it with method reference and it’ll be just perfect
Good feedback. +1
I tend to avoid doing a Select followed straight by a Max when you can just pass the select function directly into the max. Also multiline lambda expressions feel ugly but that's probably just me! This is what I would do these days:

    string pad(this string e, int padLength) => new String(' ', padLength) + e;
    var input = new string[] { "abc", "ab", "abcdef", "abcdefgh" };
    var maxLength = input.Max(e => e.Length);
    var output = input.Select(e => e.pad((maxLength - e.Length) / 2));
Sadly C# is still missing a few important ML features that making functional programming more difficult than in a true ML.
It would be interesting if you could mention which feature/s you think it most sorely lacks. Some of the recent syntax additions have made it really easy to write concise functional code. I don't even use curly braces much anymore. What about f#?
Personally, I’d replace

  width = l.reduce(0, { max($0, $1.count) })
with

  l.map { $0.count }.max()
Much better indeed!
"To me this looks like perl golfing"

I think that's preposterous and totally unfounded.

The identifiers have names like "alignCenter", "replicate", "maximum" and "length". Those are all actual full English words, nothing remotely obfuscated about them.

"map", "++" for concatenation, and "x/xs" for an arbitrary list of things are all idiomatic so can be kept short because they are used so often, like pronouns in English.

Lastly, your program looks very similar (almost isomorphic) to the Haskell version, so I think it's just dumb you use it as an argument to criticize the Haskell version as "extremely terse".

Ok.

My problem with functional programming written like this (I admit that this example is too short) is that once there are too many functions combining with other functions it is very easy to lose thread of what is happening. My comparison with perl is more to the point that these programs are way easier to write than read.

Again, this example is too simplistic to illustrate my point, but what I like with combining imperative and functional programming is that it is easier to keep trace of what is happening by using intermediate variables that hold the state and thus break the chain of functions.

That is why the importance of being terse but following the rule of not going too far with nesting and pointfree is so valuable. I don't share your difficulty to follow the flow of the program as usually the name of the function, type, and documentation say all you need to know about it.

That said, I find it difficult to follow what is happening when using several variables as you're then likely doing something wrong in the complexity of the function. A function should say much with very little and control flow should be clear instantly. The only thing outside of this would be a procedure which do-notation allows one to express.

Imperative programming is already within Haskell with do-notation an it's composition is that of imperative. Mixing functional with something else makes it much harder to make use of combinators and other forms which ends in more messy code (in my experience). If one could go pure functional without do-notation emulating the "C monoid" it would be rather nice.

It does look like APL or Perl without enough understanding of Haskell (which I know very little about). I am guessing you have some experience writing Haskell code, so it looks clear to you.

The example used idiomatic one or two letter variable names (x, n, xs) which I found difficult to read. The rewritten example was much clearer to me because I could see the variables.

> $0, $1
> for some reason perl golfing is bad but writing extremely terse functional programs is not

Because the Haskell has types checking that you're not doing anything completely stupid.

and in Kotlin

    fun alignLines(lines: List<String>): List<String> {
        val maxLength = lines.map { it.length }.max()!!
        return lines.map { " ".repeat((maxLength - it.length) / 2) + it }
    }