Live data from Hacker News

My Swift Dilemma

owensd.io

111–120 of 129 posts

Re: My Swift Dilemma

#111
post #98

Earlier quoted context omitted.

There are degrees of static typing. Swift tries to get close to Haskell, but without the full set of tools to do so without losing things along the way. This is sort of the worst of both worlds. In the meantime Strongtalk already demonstrated that you can write a fairly rich optional type system to reap the compile time benefits of static typing while retaining the runtime power of message passing. I think that would…

Why do you say it is the worst of all worlds? To me it is a very good compromise and about as good a situation as you can get while maintaining C/Objective-C compatibility. Don't forget there are performance benefits to being less dynamic (not using runtime message passing) and you can opt in to that runtime mode if you want by making classes @objc.

Well, in practice the broken generics stop large amounts of reasonable uses of generics, the lack of co/contravariance force you into hideous workarounds everywhere.

As for the promised runtime performance improvements they have yet to surface and worse: the language is still plagued by extremely uneven performance, even for optimized builds. I could go on at great length citing issues that will be hard to fix within the next year or so. Unfortunately. This is what my fairly hard won (15k loc of Swift code) experience with the language has revealed so far.

I can honestly say I really wish you were right.

Re: My Swift Dilemma

#112
post #106

Earlier quoted context omitted.

You should get around more. Objective-C object-orientation via dynamic messaging is much more advanced/powerful than the Abstract Data Types available in C++ and Java. It enables such features as target/action, NSUndoManager, Higher Order Messaging, distributed objects...and their concise implementation. If you don't understand dynamic messaging and see Objective-C as just a way of doing things that you would do in J…

This is entering highly subjective territory. The consensus is that Objective-C's brand of OO is not particularly advanced, and Objective-C as a language is definitely not considered advanced. Keep in mind Paul Graham's essay had Lisp in mind, not a C derivative. Nevertheless, the post that sparked this thread was a comparison between Swift with Obj-C . Now, even if you consider Obj-C an "advanced language" (which mo…

On the contrary, it is well known that ObjC and related languages represent a different strain of OO than Java or C++. In fact, people has gone so far as to say that C++/Java don't really represent "real" OO at all.

It should be obvious that ObjC belongs to the Smalltalk linage, which is quite different from C++/Java. Unless you have understood why Smalltalk is still held in high regard, you haven't understood the language.

Re: My Swift Dilemma

#113
post #111

Earlier quoted context omitted.

Why do you say it is the worst of all worlds? To me it is a very good compromise and about as good a situation as you can get while maintaining C/Objective-C compatibility. Don't forget there are performance benefits to being less dynamic (not using runtime message passing) and you can opt in to that runtime mode if you want by making classes @objc.

Well, in practice the broken generics stop large amounts of reasonable uses of generics, the lack of co/contravariance force you into hideous workarounds everywhere. As for the promised runtime performance improvements they have yet to surface and worse: the language is still plagued by extremely uneven performance, even for optimized builds. I could go on at great length citing issues that will be hard to fix within…

I haven't done that volume but I have written a few thousand lines including this branch of a GCD wrapper library which uses generics to allow return values to be passed between the closures running on the different threads in a typesafe way: https://github.com/josephlord/Async.legacy/blob/argumentsAnd...

It was a nightmare getting it all building right (generic methods on generic classes took me a bit of time to sort out) but I think it works well now.

As for performance noting the amount of Swift you've done (and your activity on the DevForums) I expect you know all this but I've done quite a bit on speeding someone else's code up. There are certainly plenty of ways to accidentally slow down code:

Summary of the optimisations taking someone's project from 10fps to about 1500. http://blog.human-friendly.com/swift-optimisation-number-ios...

Presentation on how to make Swift go fast: http://blog.human-friendly.com/london-swift-presentation-swi...

Re: My Swift Dilemma

#114

Earlier quoted context omitted.

You're putting words in my mouth. I did not say "it is hard to make programming languages". It is a skill like any other that can be improved with sustained practice. My point is most of these conversations about the merits/demerits of languages are less than useless. Here's a programming language checklist http://colinm.org/language_checklist.html and another one about the history of programming languages http://jam…

> Regarding "blub" I already told you that this is unlikely given the amount of dialog and work Owens has done in the language so far. He is one of the few people who has written in depth about the language (not tutorials) Well, if said post is anything to go by, that's not saying much...

You read the last chapters of books too just to see if they're worth reading?

Re: My Swift Dilemma

#115

As he predicted, I was with him until his rant about generics. While the example he makes support his point, that's nothing specific to generics but instead to the implementation of generics. For example, he uses the following example as "bad" generics: func reverse (source: C) -> [C.Generator.Element] and the following example as "good" non-generics: func reverse(source: CollectionType) -> CollectionType However, yo…

I'm just going to reply to one part with an example of the problem.

> I think this is somewhat incorrect. You should never just be type-casting your inputs. (In fact, I think it should ideally be impossible to do so without the compiler generating really big flashing warnings saying "THIS IS DANGEROUS!"). The static verification here will prevent you from doing silly things, and should ideally force you to do input validation at the location of input, instead of blindly casting things to the type it needs.

I did not say anything about type-casting inputs. I said coercing values into a given type. The naïve approach can be to type-cast, the other way is to write the code for the coercion process.

    // some collection we are holding the values in for some reason
    NSMutableArray *inputValues = [[NSMutableArray alloc] init];

    NSString *someInputValue = // probably read from user input or a file
    NSInteger value = [someInputValue integerValue];

    BOOL validInput = YES;
    if (value == 0) { // we need to check that there really is a value of 0...
        NSString *trimmed = [someInputValue stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceCharacterSet];
        NSString *trimmed0 = [trimmed stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"0"]];
        if (trimmed0.length != 0) {
            // oops, actually had an error... handle it
            validInput = NO;
        }
    }
    
    if (validInput) {
        [inputValues addObject:@(value)];
    }
Of all of the places where could have had errors along the way, the last `[inputValues addObject:@(value)];` doesn't really concern me that much.

Also, the compiler doesn't help me get things correct... the only thing it would have helped me do is make sure I put an integer into the array, not that I had the right values in the array. Had I not known that `integerValue` returns `0` in its error cases, I would have not known that I need to write some additional code to verify the string value was indeed a zero.

And generics only helps you when you have collections of identical types. Storing arrays of plist entries, for instance, requires you to revert back to `AnyObject` (or similar).

Generics can be helpful, if it's done well. However, even in .NET's generic system, with all the limitations and constraints it put in, there are many times where it still gets in the way.

I'm tired of fighting with my tools just to get the job done. Currently, Swift makes me fight a heck of a lot more then I want to or need to just to make the compiler happy. The end code I write is the same both ways, but the Swift code has a lot more annotations and is a lot less flexible.

Re: My Swift Dilemma

#116
post #114

Earlier quoted context omitted.

> Regarding "blub" I already told you that this is unlikely given the amount of dialog and work Owens has done in the language so far. He is one of the few people who has written in depth about the language (not tutorials) Well, if said post is anything to go by, that's not saying much...

You read the last chapters of books too just to see if they're worth reading?

How does that analogy make any sense?

An uniformed post with several questionable arguments is totally fine to judge someone's understanding of a language.

And if it's the "last chapter" of his posts (that is, something he wrote after several previous posts exploring the language), it's even better to see if his opinions are "worth reading". In the sense that a first post with his initial impressions of the language would be more excusable not to be that good.

Plus, reading the "last chapters of books to see if they're worth reading" sounds a perfectly OK way to judge something like a technical book. If the last chapters are crap why would the previous be any better?

If you weren't talking about technical books, then the analogy doesn't applu. Tech posts are not some linear narrative like a book, where you don't read the last chapters because you'll might get some spoiler. In fact it's common to skip the first introductory chapters in tech books, since they are mostly intended for beginners.

Re: My Swift Dilemma

#117
post #88

Earlier quoted context omitted.

> The tools that I'm aware of that can do this (such as QuickCheck or ScalaCheck) come from statically typed languages, though I don't see why they couldn't be used with dynamically typed languages. The tools use type information to determine the universe to draw test values from and the mechanism used to do it. You can actually do something very similar for dynamically typed languages, but if you don't have queryabl…

You are right, without additional information property testing would be less useful. Which is yet another reason to favor static typing in my opinion.

There are actually many poor claims you made in your posts about "good tests".

> I'm aware that addition is a toy example, but suppose we want to test our implementation: > Except for very simple verification, to exclude obviously broken implementations, I'd rule out testing specific values such as 3+4=7. And, like you said, performing an exhaustive exploration of all values is out of the question. > So I'd try property testing instead. Relevant properties in this case are associativity, commutativity, etc. > As an example, I'd try writing properties such as: > for all X, Y: add(X, Y) = add(Y, X)

These properties can also be satisfied by implementations of add() that: - return a constant value - return the smallest number of (x, y) - return the largest number of (x, y)

The cases you threw out as an "obviously broken implementation" are required to actually validate that functionality of the method. The functionality of the method is also one of the properties of the method.

You can write it in a more generic way than simply: assert(7, add(3, 4)). However, those tests are _also_ required. Without them, you never actually test that the `add()` function does what it's supposed to: add two numbers together.

Regardless of type system, you also have to worry about underflows and overflows - another property of the functionality of the method.

> You are right, without additional information property testing would be less useful. Which is yet another reason to favor static typing in my opinion.

Static typing doesn't help you constrain sets of inputs; it may not be valid that your method accepts all ranges of integers. You could have a method `addbase2(int x, int y)` that is to be used only when x and y are powers of two because of an optimization you perform in that method. Static typing doesn't help you generate the correct input set for x and y.

The only thing that static typing provides, in regards to test cases, is this:

    def add(x, y)
      assert x is int
      assert y in int

      return x + y

    // test cases
    assertIsThrown(add("foo", "bar"))
That was the test case you had.

Regardless, the point of the article was not about static typing being bad. There is value it. However, there is also value in not being so rigid in your type system that things don't work well.

    add((short)0, (long)1)   // compiler error if you have an extremely rigid type system
Generic systems typically swing the pendulum far to the right requiring an extremely rigid type system. That always causes pain. The question you have to ask, is the ROI worth it. For some, it is. For others, it's not.

Re: My Swift Dilemma

#118
post #110

Earlier quoted context omitted.

> it fails to tell apart addition from the constant function 7 It's not supposed to do that. Have you heard of TDD? In a TDD/XP setting, the constant function 7 would be the appropriate implementation for making that test pass, because it is the simplest thing that could possibly work. Then you add another test, let's say add(40,2) EXPECT(42). Now you could extend your add() function to do case analysis, and maybe in…

I'm familiar with TDD, both its strenghts and limitations ( http://ravimohan.blogspot.com.ar/2007/04/learning-from-sudok... , http://beust.com/weblog/2014/05/11/the-pitfalls-of-test-driv... , etc). I understand that this kind of testing often works (though I'm less enthused with TDD as a design technique, which is unfortunately what TDD proponents emphasize, and what you seem to be describing here). I'm saying it is…

Well, you're obviously not familiar with TDD, just with silly straw man arguments against it. No, the 4+3=7 test is not supposed force creation of actual addition, the additional tests + design principles do.

And I am not saying "but software is written and tested this way". Software is written and (often not) tested in many ways. I am saying that in my practical experience software that is written this way is both much simpler and much more robust than people not familiar with these techniques such as yourselves imagine. Or maybe can imagine.

As to architecture, I strongly recommend Henrik Gegenryd's PhD Thesis: "How Designers Work"[1].

By the way, please don't confuse "easy" with "simple" like the second blog post you reference.

[1] http://chrisrust.wordpress.com/1998/12/31/how-designers-work...

Re: My Swift Dilemma

#119
post #110

Earlier quoted context omitted.

I'm familiar with TDD, both its strenghts and limitations ( http://ravimohan.blogspot.com.ar/2007/04/learning-from-sudok... , http://beust.com/weblog/2014/05/11/the-pitfalls-of-test-driv... , etc). I understand that this kind of testing often works (though I'm less enthused with TDD as a design technique, which is unfortunately what TDD proponents emphasize, and what you seem to be describing here). I'm saying it is…

Well, you're obviously not familiar with TDD, just with silly straw man arguments against it. No, the 4+3=7 test is not supposed force creation of actual addition, the additional tests + design principles do. And I am not saying "but software is written and tested this way". Software is written and (often not) tested in many ways. I am saying that in my practical experience software that is written this way is both m…

Please do not assume I'm unfamiliar with TDD because I disagree with you. It diminishes your argument.

Here you'll find references from big names (Peter Norvig, Joshua Bloch) discussing limitations of TDD as a design process: http://gigamonkeys.wordpress.com/2009/10/05/coders-unit-test... (read it, it's more balanced than you'd think).

Here are some interesting comments from the above:

- Bloch: tests are inadequate as documentation.

- Norvig: I like TDD but it's inadequate to discover unknown algorithms.

(The infamous Sudoku Solver debacle is a particularly painful example of Norvig's claim, and in particular I think Ron Jeffries' attempt at doing TDD was embarrassing. Like the blog says, if I were a TDD proponent, "I'd be pretty strongly tempted to throw Jeffries under the bus")

Very few if any of the people mentioned outright dismiss TDD, but they do point out its limitations, which to me are mostly about TDD as a design process.

If we go back to TDD as testing, my initial objections apply: it's useful, but it's not enough. More advanced and formal tools, and static typing, are of great help here.

Even if you disagree with everything else, you must at least agree with this: computers are about automation. Automating as much as we can, including testing, is a good thing. Writing tests is itself something that can -- in some areas -- be automated, in which case it should be preferred over hand-writing those tests.

Re: My Swift Dilemma

#120
post #117
post #88

Earlier quoted context omitted.

You are right, without additional information property testing would be less useful. Which is yet another reason to favor static typing in my opinion.

There are actually many poor claims you made in your posts about "good tests". > I'm aware that addition is a toy example, but suppose we want to test our implementation: > Except for very simple verification, to exclude obviously broken implementations, I'd rule out testing specific values such as 3+4=7. And, like you said, performing an exhaustive exploration of all values is out of the question. > So I'd try prope…

Note I never claimed unit testing should be disregarded (I practice it and recognize its benefits), or that static types catch all errors, or that add(x,y) was anything but a toy example.

Please note I didn't throw out add(3,4)==7, but instead pointed out it's terribly inadequate as a test. Additional testing tools must be employed; unit testing alone of this kind is not enough.

With property testing you're still not proving correctness. Tests cannot prove correctness. But it's a step in the right direction. Sure, maybe you have a function "add" that is associative, commutative, and has a neutral element, and it's still not integer addition. I'd argue your confidence in such a function will be a lot higher than if you had simply unit tested a few border cases. You can still do that in addition to property testing, anyway.

> The only thing that static typing provides, in regards to test cases, is this [example]

This assertion is wrong. Static typing done well provides a lot of things "for free", such as restricting incorrect behavior. For example, if you write generic methods you can rule out entire classes of misbehavior. It's not that you "type check" that you are not using something that is not an int (as in your example), but that you simply forbid entire groups of operations at compile time!

Here's another toy example to illustrate the point: what values can a function with the following signature return?

    f :: [a] -> a
(For the purposes of this question, you can read that as "a function that takes a list of type a and returns a value of type a).

Now, repeat the exercise with a dynamically typed language. What values can the following function return? (If you want, for the purposes of this question, assume it returns an atomic value and not a collection).

    dynamic_f(a_list)
This has an obvious implication on the effort you must make when testing either function.
Post reply on HN