Compiler etc. limitations, "firing back" at language development

Proposition:
In principle, the facilities of a programming language should not at all be influenced or restricted by the complexity of the compiler.

Which would imply that all feedback and complaints from compiler creators should be ignored! This is of course absurd and a humorous approach meant as just to illustrate the principle :o)

Please Note: "to compile" here means the overall complete process from source to executable that includes linking and everything in between, but mostly it's at "the front" of this process.

Some examples in Swift:
The introduction of a subtype of String, Substring, which has the same properties as String itself.

If one looks at the documentation the existence of the type Substring is justified with "Operating on substrings is fast and efficient because a substring shares its storage with the original string."
This in fact implies that the only reason for the existence of the type Substring was for machine efficiency and/or to limit compiler complexity.
From an application programmer's point of view the type Substring is completely superfluous and more of a nuisance instead of an improvement.

In case of Substring, the programmer (which can be anyone from a Rookie to a Guru, so to speak) is confronted and needs to know about the intricacies of in whatever way -in this case strings- are implemented on a very low level.
As for me, if I were still a Swift-Rookie programmer, I want to know as little as necessary about these confusing "under the hood" details which are in fact delaying getting my apps up and running. Only in mostly rare cases, that is when an app is running not fast enough, I might be interested. In all other cases I simply really don't want to know, spare me the details, sorry. These kind of low level aspects (e.g. storage, speed) should be completely transparent to the application programmer.

Let's take a look at another example:
The restriction that new operators can only be defined with the use of punctuation characters; currently, in Swift it is not possible to define operators such as
"not" "and" e.g. like so "if not isEating and isAtHome {..} "
Apart from whether or not such operators are useful, this is also a compiler restriction as described in "Commonly Rejected Changes" I quote:
"Requiring the compiler to see the operator declaration to know how to parse a file would break the ability to be able to parse a Swift file without parsing all of its imports. This has a major negative effect on tooling support."
This clearly illustrates that this restriction is induced by compiler specific low level aspects which have no relation with the programming language itself at all.

Note that we're not discussing here the content of these two above examples (of course there are many more) but solely:

How much should compiler (builder's) restrictions "ah, it's too hard or too costly to implement" hinder or even have negative backward impact on the development of a programming language? As for me the most extreme example is the introduction of Substring...

I realize of course:
This whole view is almost purely theoretical, which can of course not be 100% conformed
In a sense, each programming language is more or less a Compromise To The Machine.
Would be nice to know what you think about this.
Thanks
TedvG

I will say I the Substring really nails it. It should not exist

Isn’t the reason for the Substring type so that you can modify the original String without referencing it? This wouldn’t be possible with a String alone because String is a value type, so modifying one can’t change another. And if you wanted slicing to return a String, then you’d need magical behavior to remove the value type-ness of a String in that case. Perhaps they should have named it StringSlice after all...

The reason for Substring is to avoid memory leaks from storing small subsets of large Strings. If you have a giant String and take a piece of it as a Substring and store it, you keep a reference to the whole original giant String, keeping it from being deallocated. Instead, since you're not supposed to do long-term storage of Substrings, if you convert it to a String, it will copy the part of the Substring that is used and stop referencing the original larger string so it can be deallocated.

2 Likes

Yes, Tellow, thanks, I know what the reasons for Substring are, but as I described, those reasons are low level and (should) have nothing to do with the application programming level. In a way, you have exactly illustrated my point, by describing almost everything which should be hidden from and is mostly irrelevant to an application programmer. Substring btw is just an example in this topic.

I completely disagree with this assertion. Low-level details matter, even to "application programmers", when they affect the high-level behavior of the program. Substring was motivated by "application-level" concerns, where sharing storage by default can cause what are effectively leaks of large string allocations. The effect was observed in Java---not at all a language whose design is dominated by low-level details---but existed in the Swift < 3 String design as well.

  • Doug
9 Likes

Still, the problem here is that string storage is shared, so this sacrifice (storage, speed) compromise is made at a low level. Imho, strictly, String storage should not be shared implicitly because no elements in the Swift programming language says something about that strings are shared. There's no directive allowing me to share or not share the storage of string(s)
Ergo:
The problem you describe wouldn't exist if each and every String had it's own unique storage, right? Also this would not occur if String was a class (ref type) instead of a struct (value types) if I am not mistaken?

The "thinking model" for the typical application programmer is that each string has its own individual storage! E.g. s = "abcd" and s2 = "abcd" should not occupy the same storage: "Huh? I did create individual vars did I?"

You need to be aware of a lot of "low level" details:

  • floating point numbers varying in precision depending on their magnitude
  • integers overflowing
  • integer division
  • struct vs class (copy vs refcount, stack vs heap)
    ...

And regarding your assertion that the compiler should do what ever is necessary to parse a file:
If it's to hard for the compiler to do it will eventually be to hard for a human to read he code as well. How would you as a human figure out if in "not isEating and isAtHome" the isEating is a binary operator and not,and are it's parameters and isAtHome is a postfix operator or if not is a prefix operator and and is the binary operator or if all but isAtHome are prefix operators? What if all those are overloaded by type (isEating is a binary operator (Animal,Animal) -> Animal, but also a unary prefix operator (Human)->Bool)...

You wont want to have arbitrary complex parsing rules...

3 Likes

I will add also that Swift's not for desktop application programmer use only. From Swift.org - About Swift

The goal of the Swift project is to create the best available language for uses ranging from systems programming, to mobile and desktop apps, scaling up to cloud services.

In many of these scenarios — systems programming, mobile apps, cloud services — performance tuning and the ability to predict the performance characteristics of one's code are essential. Having the stdlib force picking one or the other way to resolve this trade-off — whether to sacrifice space by leaking the string or introduce O(n) copies for common operations — did not serve these goals, whereas Substring gives developers the ability to choose which performance characteristics to have by giving them that choice at the place in the codebase where that choice is most relevant. There is a trade-off with clarity that has been thoroughly examined during the review for that particular overhaul.

7 Likes

I get what you are saying and I agree in principle, but sometimes reality makes this difficult. For your substring example, sure you could use copies everywhere and avoid the leaks and then your software would be slower. And sure you could possibly make the compiler "sufficiently smart" so that you somehow avoid this performance hit.

The more high level you go, the smarter the compiler must be and at the end it becomes a matter of priorities. Do you want to use swift now or in 20 years?

See also: http://wiki.c2.com/?SufficientlySmartCompiler

In principle, the facilities of a programming language should not at all be influenced or restricted by the complexity of the compiler.

Completly disagree. The language design must take into account compilation concerns. It must be possible for a compiler to quickly compile thousands of lines of code. There are certain design decisions that make this impossible. Example: C++.

Another concern is producing useful error messages. There are certain design decisions that make it impossible to produce useful error messages. Again, example: C++.

This in fact implies that the only reason for the existence of the type Substring was for machine efficiency and/or to limit compiler complexity.

This is a very good reason for introducing a language feature. We should care very much about how our decisions affect the performance of the end-products (e.g. applications) built with the language.

As for me, if I were still a Swift-Rookie programmer, I want to know as little as necessary about these confusing “under the hood” details which are in fact delaying getting my apps up and running.

I will argue that language design should not cater to the notion of a "clueless helpless beginner". Every beginner will be in a stage of being "completely clueless", but this should not be an excuse to ignore low level concerns.

Programs execute on physical hardware and there are physical limitations. Some ways of expressing a logic execute a lot slower than other ways. We should care about that and provide ways for the language to allow the programmer to write code that will execute fast.

5 Likes

(in reply to Ted, furthering Doug's line of argument)

The main question for Substring: is it important to represent the sharing of storage in the type system? There are entire domains of programming, including within application development, where the answer is definitely yes.

Swift shouldn't close off entire domains of programming (including within application development) just to shave a few characters off of your source code. Now, Swift should try to lessen the burden of this divide as much as possible and there's definitely improvement that can be made here. For example, I sometimes define a "copy" computed property on slices so that I can continue a method-invocation chain instead of going back to wrap in an init. Perhaps there's a general solution to this, and that would make a nice addition/proposal.

Either way, this is a completely different topic as it has no direct relevance to "compiler complexity". Substring is an example of the complexity of computers and of the task of programming them. Please find a different example for your argument.

1 Like

It seems to me that you completely misunderstood this. We could make the compiler do this dependency parsing, to resolve all operators. The issue is that the compiler isn't the only program that needs to know about the semantic structure of Swift programs.

No text editor would be able to accurately syntax highlight Swift programs without hooking into the compiler to parse dependencies to resolve operators. Xcode does this already, sure, but there are lots of other text editors people want to use. The chances of all of them expanding their highlighting support to hook into the compiler is pretty much 0. That's not even mentioning the syntax highlighting on websites such as this forum, github, etc.

3 Likes