Live data from Hacker News

Swift and the Legacy of Functional Programming

realm.io

51–60 of 188 posts

Re: Swift and the Legacy of Functional Programming

#51

I'm curious, does every else find this let persons = names .map(Person.init) .filter { $0.isValid } easier to read than this? var persons: [Person] = [] for name in names { let person = Person(name: name) if person.isValid { persons.append(person) } } I understand and appreciate the value of compact code, but I find the first one harder to read. A lot of inferred/token based coding is harder for me to mentally parse.

I find it easier to read with the exception of the anonymous variables. In scala, I would write

    val persons = names
        .map { name => Person(name = name) }
        .filter { person => person.isValid }

Re: Swift and the Legacy of Functional Programming

#52

I'm curious, does every else find this let persons = names .map(Person.init) .filter { $0.isValid } easier to read than this? var persons: [Person] = [] for name in names { let person = Person(name: name) if person.isValid { persons.append(person) } } I understand and appreciate the value of compact code, but I find the first one harder to read. A lot of inferred/token based coding is harder for me to mentally parse.

let persons = [ p | n is even better, if your programming language has comprehensions.

Eh, maybe for more complicated examples, but I think that comprehension is a lot more complicated than:

  filter isValid (map peopleNamed names)

Re: Swift and the Legacy of Functional Programming

#53
post #11
post #7

Earlier quoted context omitted.

Most people would agree that in functional programming we can pass functions around as first class objects. What we disagree on is the definition of function.

No, wrong. You can pass pointers to functions in C.

It is a bit snarky, but also has a ground truth: pointers to functions aren't functions.

More importantly, you cannot make functions in C. For example, one cannot write a function that, given pointers to functions that compute 1/x and sin(x), returns a pointer to a function that returns 1/sin(x)

Re: Swift and the Legacy of Functional Programming

#54

Earlier quoted context omitted.

For me the problem with Haskel (and, by extension, Swift) is the syntax is very complicated. The example above is better taught through a functional language that has almost no syntax like a Lisp. (filter (fn[p] (p :valid?)) (map (fn[n] (Person n) names))

Haskell's syntax isn't actually that complicated. There are some common functions with operator names that can be hard to read if you're not used to them, but those are library defined. The syntax itself is actually fairly simple. And incidentally, this would probably be something like (EDIT: made example more realistic): filter personIsValid (map initPerson names) in Haskell. Which looks much cleaner than the lisp t…

That's because your code assumes those convenience functions exist while the parent's code writes out the anonymous functions.

The direct translation of yours' to Clojure would be:

    (filter valid-person? (map init-person names))
Meanwhile, the direct Haskell translation of the parent comment is:

    filter (\p -> isValid p) (map (\n -> Person n) names)

Re: Swift and the Legacy of Functional Programming

#55
post #46

I'm curious, does every else find this let persons = names .map(Person.init) .filter { $0.isValid } easier to read than this? var persons: [Person] = [] for name in names { let person = Person(name: name) if person.isValid { persons.append(person) } } I understand and appreciate the value of compact code, but I find the first one harder to read. A lot of inferred/token based coding is harder for me to mentally parse.

I definitely hear what you're saying. In this particular case I find the more succinct map/filter a little easier to grok, but as soon as you have a bunch more clauses with some flatMap() and reduce(), the "functional" way can get out of hand quickly. In simple cases, I prefer (Python's) list comprehensions. In more complex cases, I prefer the loop(s).

I find Python's list comprehensions mind-numbing in many simple cases, such as [x for x in xs if some_condition(x)] rather than just xs.filter(some_condition). We're repeating the variable name three times and zero of those times convey any useful information, and we don't find out until the very end of the line that the everything except "[", "]" and "some_condition" was ignorable.

And in cases where a data-deriving loop has so much going on directly in the loop body that it makes map/filter/reduce hard to read, there's very often some refactoring that would improve either version.

Re: Swift and the Legacy of Functional Programming

#56

I'm curious, does every else find this let persons = names .map(Person.init) .filter { $0.isValid } easier to read than this? var persons: [Person] = [] for name in names { let person = Person(name: name) if person.isValid { persons.append(person) } } I understand and appreciate the value of compact code, but I find the first one harder to read. A lot of inferred/token based coding is harder for me to mentally parse.

For me it's neutral, except for one critical difference.

I've been shepherding a bunch of front end Javascript tests, and recently had to go through fixing a systemic problem with how we were handling multiple promises.

The broken code didn't look too different from your second example, but the same three people made the same mistake repeatedly, leading to tests that generated empty lists and thus didn't verify anything.

Now, I'm not claiming this is a good pattern of testing. Indeed in all of the straightforward cases I removed the array entirely, and with great relish (especially since it also sped up the tests dramatically). And there are obviously some gaps in their theory of testing that they didn't notice the problem until I pointed it out.

But it did illustrate to me again that there are (alarmingly) a lot of people struggling with basic data manipulation, and if your language supports anything like list comprehensions, I think you should probably get used to using them. It keeps those gaps out, and makes people decompose the problem instead of mashing together a block of conditional code that reads like a Choose Your Own Adventure.

Re: Swift and the Legacy of Functional Programming

#57

I'm curious, does every else find this let persons = names .map(Person.init) .filter { $0.isValid } easier to read than this? var persons: [Person] = [] for name in names { let person = Person(name: name) if person.isValid { persons.append(person) } } I understand and appreciate the value of compact code, but I find the first one harder to read. A lot of inferred/token based coding is harder for me to mentally parse.

    persons = [person for person in (Person(name) for name in names) if person.isValid()]
This is Python.

Re: Swift and the Legacy of Functional Programming

#58
post #44

Earlier quoted context omitted.

That's just one aspect of functional programming. By that definition, almost any modern language is functional.

Take it from the other side: why would a language that allows to create higher-order functions/closures not be called functional?

Because that makes the term meaningless. You may as well ask "why don't we call any language that lets you do two actions in sequence 'procedural'?"

Re: Swift and the Legacy of Functional Programming

#59
post #53
post #11

Earlier quoted context omitted.

No, wrong. You can pass pointers to functions in C.

It is a bit snarky, but also has a ground truth: pointers to functions aren't functions. More importantly, you cannot make functions in C. For example, one cannot write a function that, given pointers to functions that compute 1/x and sin(x) , returns a pointer to a function that returns 1/sin(x)

That's not a function, that's a closure. And you can simulate that in C by creating a struct that contains the function pointer and the set of captured data (and then when you invoke the function you pass the struct to it as a parameter).

Re: Swift and the Legacy of Functional Programming

#60

I'm curious, does every else find this let persons = names .map(Person.init) .filter { $0.isValid } easier to read than this? var persons: [Person] = [] for name in names { let person = Person(name: name) if person.isValid { persons.append(person) } } I understand and appreciate the value of compact code, but I find the first one harder to read. A lot of inferred/token based coding is harder for me to mentally parse.

I don't see why either is more difficult. For the functional version, you just need understand the meaning map and filter, and for the imperative version, you need to understand the meaning of for loops and append.
Post reply on HN