Live data from Hacker News

Swift and the Legacy of Functional Programming

realm.io

151–160 of 188 posts

Re: Swift and the Legacy of Functional Programming

#151
post #80

Earlier quoted context omitted.

Though by this definition, Lisp, the granddaddy of functional programming languages, is not a functional programming language.

It indeed isn't. Here's my litmus test. Is 2^100 always equal to 2^100? Let's ask SBCL: * (eq (expt 2 100) (expt 2 100)) NIL Damn object identities, ruining muh equalities. (Disclaimer: I'm not saying functional programming is the right approach for writing every program, but if a language can't even get arithmetic and relational operators right...)

Well, I would just use EQL.

If you want to use Haskell, use Haskell. Common Lisp works differently.

Re: Swift and the Legacy of Functional Programming

#152

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.

Practice will help make the former more readable. It helps, for me, that I have more of a math background (academically) than CS (dual majors, but strongly preferred the math coursework). For me, I see three ways to write, as an example, a summation: n Σ g(i) i=1 vs. seq(1,n).map(g).sum() // or something similar vs. for(i = 1; i In my mind, the first is what I see, and is what shows up on paper. The second is what I…

Shouldn't that be += in the third example?

Re: Swift and the Legacy of Functional Programming

#153
post #103
post #68

Earlier quoted context omitted.

I find the first one to be far easier to read. You can basically just read it from top to bottom and know exactly what it does. "Take names, turn each one into a Person, keep the ones that are valid." The second one takes a lot more work. First, I have to read through the code and recognize that this is a loop that accumulates values into a new array. Then I have to pick it apart and see exactly where the accumulatio…

Is the first one two loops or one?

The real answer is that it doesn't matter. Just like for most code, the output assembly or machine code doesn't matter.

Re: Swift and the Legacy of Functional Programming

#154
post #152

Earlier quoted context omitted.

Practice will help make the former more readable. It helps, for me, that I have more of a math background (academically) than CS (dual majors, but strongly preferred the math coursework). For me, I see three ways to write, as an example, a summation: n Σ g(i) i=1 vs. seq(1,n).map(g).sum() // or something similar vs. for(i = 1; i In my mind, the first is what I see, and is what shows up on paper. The second is what I…

Shouldn't that be += in the third example?

Yep. Another reason a simple 'sum' is nicer than what C gives us.

Or a reason to go back to checking all code I try to type in.

Re: Swift and the Legacy of Functional Programming

#155
post #133

Earlier quoted context omitted.

Structural equivalence is given by EQUALP and has of course its own limitations (but works with trees, user-defined structs and hash-tables, for example). Of course, if you use the identity comparison, you get different results. I agree CL does not fit your definition of functional programming. An implementation is permitted to make "copies" of characters and numbers at any time. The effect is that Common Lisp makes…

I'm aware of EQUALP. But the problem remains that it's possible to distinguish between supposedly “equal” values. Lisp and Scala are first and foremost object-oriented languages - whatever values you want to manipulate are always subordinate to objects whose physical identity in memory matters in the language's semantics, no matter how irrelevant they might be for your problem domain. On the other hand, in Haskell an…

In Lisp, symbols are very important. And symbols are identity. An identity with other dressing, such as having a character string name which is somewhat of a semantic footnote.

"Functional programming" doesn't restrict the kinds of obejcts you can work with. An identity value, whose virtue is that it is different from other identities, is a legitimate concept which can be treated under functional programming.

I guess your problem is that you don't want non-symbolic objects from behaving like identities; only symbolic ones.

That is to say, two symbols are equal iff they are actually the same symbol, otherwise not---but other kinds of entities are treated differently.

There is something to be said for having just one kind of equality, which is appropriately defined for every kind of object.

Pragmatic issues get in the way though.

Let's consider "this list [1,2,3]" versus "that list [1,2,3]". What if we have lazy lists (as those things tend to crop up in functional languages, particularly non-strictly evaluated ones). Is "this infinite list [1,2,3, ...]" equal to "that infinite list [1,2,3,...]" even if they are generated by completely separate lazy procedures?

Your equality then has to basically test that two Turing computations are equivalent: that the underlying lazy generation procedures themselves are computing the same thing, even if in different ways.

In that vein, what do you do about structures with cycles and shared structure? Is the list #1=(1 . #1#) equal to another one that is also #1=(1 . #1#). (Lisp's equal functions don't have to handle this; but we could specify an equal function which does).

Equality is not so simple that we can just cover all of its nuances with a blanket rule based on some handwaving principle.

Re: Swift and the Legacy of Functional Programming

#156

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.

Me too. The first one is harder to read, and also the first one is harder to extend if the business rules change.

Re: Swift and the Legacy of Functional Programming

#157

Earlier quoted context omitted.

I'm aware of EQUALP. But the problem remains that it's possible to distinguish between supposedly “equal” values. Lisp and Scala are first and foremost object-oriented languages - whatever values you want to manipulate are always subordinate to objects whose physical identity in memory matters in the language's semantics, no matter how irrelevant they might be for your problem domain. On the other hand, in Haskell an…

In Lisp, symbols are very important. And symbols are identity. An identity with other dressing, such as having a character string name which is somewhat of a semantic footnote. "Functional programming" doesn't restrict the kinds of obejcts you can work with. An identity value, whose virtue is that it is different from other identities, is a legitimate concept which can be treated under functional programming. I guess…

> An identity value, whose virtue is that it is different from other identities, is a legitimate concept which can be treated under functional programming.

Of course. But I don't want object identities to be the only values I can manipulate. I like having values like the list [1,2,3].

> Your equality then has to basically test that two Turing computations are equivalent: that the underlying lazy generation procedures themselves are computing the same thing, even if in different ways.

Indeed, this means that equality of higher-order values is undecidable. Therefore, don't rely on testing whether higher-order values are equal - you just can't! This is also why laziness by default is such a bad idea.

> In that vein, what do you do about structures with cycles and shared structure?

In Standard ML, `val rec xs = 1 :: xs` simply doesn't compile. The right-hand side of a `val rec` definition must be a function literal. This guarantees that inductive data types (such as lists and trees) mean the right thing, and that structural recursion on them always terminates.

> Equality is not so simple that we can just cover all of its nuances with a blanket rule based on some handwaving principle.

I'm not handwaving anything.

Re: Swift and the Legacy of Functional Programming

#158

Earlier quoted context omitted.

> But the second one is more debuggable than the first, which I think is even more important than readability. The first is less likely to require debugging in the first place. > There are lots of data structures in this style of programming that don't have any names. So you can only reason about things that have names? Now we know where idiomatic Java comes from. > Who knows what kind of data structures map and filt…

> So you can only reason about things that have names? I think the point was that you can debug things that have names because they are separately watchable. But apart from that breaking things down and naming them can make for easier comprehension. This is true in written English: Naming actors when explaining something and using an active voice is generally recommended. e.g "The user enters a password and the progr…

> I think the point was that you can debug things that have names because they are separately watchable.

Note I said “reason”, not “debug”. When manipulating algebraic expressions, I don't need to give every subexpression a name - that would be torture!

Re: Swift and the Legacy of Functional Programming

#159

Earlier quoted context omitted.

Most languages only have higher-order procedures, not higher-order functions. As for closures, well, closures are an implementation technique . Not distinguishing between language features and implementation techniques is a part of an established tradition that comes from Lisp, but that doesn't make it any less wrong.

> Most languages only have higher-order procedures, not higher-order functions. What do you mean by that?

Functions have the following properties:

(0) A function maps every element of its domain to a unique element of its codomain.

(1) Functions don't exist in time, let alone change over time.

(2) Two functions with the same domain and codomain are equal if they map the same domain elements to the same codomain elements.

Since when do so-called “first-class functions” in most programming languages behave like this?

Re: Swift and the Legacy of Functional Programming

#160

Earlier quoted context omitted.

They're both parallelizable, but the second one is harder to automatically parallelize. There are guarantees about the way a map function works. It doesn't mutate its input, it only has access to one element at a time, you can't access the data structure you're building, etc. All of these traits are true of the imperative version as well, but it's a lot harder to write a program which understands that. Meanwhile, you…

As a matter of fact loops that mutate state are not parallelizable because you can't analyze what they do in order to infer intent and thus some imposed ordering with which you could efficiently distribute the required work while at the same time guaranteeing correctness - solve that and you'd solve the Halting problem ;-) Can't be done.

Well, you can't solve it in the general case. But parallelizing compilers can and have been written that perform a conservative analysis -- they can prove some subset of all parallelizable loops as parallelizable, and for the ones they can't prove as such, they err toward 'remain serialized'.

For a simple example, code that performs regular array-based accesses, where index computations are simple affine functions (a*i + b) of a single iteration variable 'i', is a case that's received a lot of attention. See e.g. Polly for LLVM.

Post reply on HN