Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Locked thread
Axiem
Oct 19, 2005

I want to leave my mind blank, but I'm terrified of what will happen if I do

rjmccall posted:

Is Bar is a protocol? What are you asking for that you can't do with let barables: [Bar] = ...?

There was a point where I had a class Foo that conformed to protocol Bar. Doing this code would consistently cause a crash:

code:
let x: [Foo] = /* generate an array of Foo things */
let y: [Bar] = x
With an error about being unable to bridge from Objective-C.

I am in Swift 1.1, not 1.2, so I'm a bit reticent to file a defect, because I can only imagine this is either a known bug or a feature that hasn't yet been implemented. It's just frustrating, though I found a different way of accomplishing what I wanted ultimately.

Adbot
ADBOT LOVES YOU

Axiem
Oct 19, 2005

I want to leave my mind blank, but I'm terrified of what will happen if I do
Two questions.

First, if two types are typealiased to the same thing, is there a way to have the compiler show a warning or error if you try matching them? In other words:

code:
typealias TypeA = Int
typealias TypeB = Int
let x = TypeA(1)
let y = TypeB(2)
let z = x + y // <-- I want an error or warning on this line
Doing it without necessarily using a typealias would be okay, but I'm not sure what other options are out there.

As to the why: just because I have two types that are both actually Ints under the hood doesn't mean I think that adding them (or having them interact) is actually sensible. For example, if x represents the number of dollars you have on your body, and y represents the number of goal tokens you've obtained (and defining them as new structs seems odd because in all other ways I want them to work exactly like Ints, and I don't want to have to redefine +, -, etc. for each struct)


Second, is there any way to "peek" inside of a curried function to find out what the bindings were?

To use the example everyone seems to love:

code:
func add(x: Int)(y: Int) -> Int {
    return x + y
}
let add2 = add(2)
Is there any way to query add2 to get the value of x = 2? Especially because if instead of 2, it was a value computed somewhere else.

As to the why: if I'm passing around a curried function, especially one where one of the values was computed elsewhere, it would be nice to be able to get that value so that I could print it out onto the screen, or more importantly, confirm it in a test. That way, I could actually be typealiasing functions as types and passing them around like first-class citizens, but also be able to query them and test them as value objects like first-class citizens.

Axiem
Oct 19, 2005

I want to leave my mind blank, but I'm terrified of what will happen if I do
Honestly, these days, I'd much rather we never see optional methods in protocols. Now that we can pass around closures as first-class objects in variables, I can't actually think of a use case where I'd want to conform to a protocol but not have to implement all the protocol's methods.

Axiem
Oct 19, 2005

I want to leave my mind blank, but I'm terrified of what will happen if I do
Forgive my ignorance, but what does putting "import Foundation" or "import UIKit" at the top of a given Swift file inside a project actually do? I understand that in say, C, you provide header files in #import statements in order to let the linker do its job, but that was at a per-file level, not a per-module level.

Following up on that, is there any compelling reason not to be able to say "just consider every file in this project to `import Foundation`"? It just seems odd to me that for Foundation, UIKit, or even any module that touches much of my code, I have to clearly define that I'm importing it.

I guess what I'm saying is, is there a compelling reason not to have a Swift version of Prefix.pch to do this for us?

Axiem
Oct 19, 2005

I want to leave my mind blank, but I'm terrified of what will happen if I do

eschaton posted:

Part of doing this is clearly defining the boundaries between the different parts of your app, which includes considering their dependencies.

This does make sense to me, and is something I miss about the header-file-inclusion-tree from C (but not enough to want it back). It does make sense to me not to make all these non-UI things use UIKit (although, if they need CGFloats...). But Foundation? A logging framework (like CocoaLumberjack) that's used everywhere?

Honestly, Foundation and XCTest are the two that I'm just getting tired of typing in all of my files (all my test files, in the latter case, at least). And also in my test files telling them all to import my main module (because why would I not want to do this?). Many of the rest of the modules, like UIKit, I'm totally cool with not allowing this for the reasons you mentioned.

Axiem
Oct 19, 2005

I want to leave my mind blank, but I'm terrified of what will happen if I do
The Protocol-Oriented Programming session was very interesting, and I've been playing with protocols and their extensions. Three things have popped out to me so far:

Why can I only provide default implementations for protocol methods in extensions?

I don't really understand how doing everything by protocols is really superior to having a mocking framework. It means that for every protocol, I have to hand-roll a custom object that conforms to that protocol and captures the test data I need along with setting up canned responses. Part of the point of a mock is saying "Hey, give me a thing that conforms to this API (such as specified in a protocol) and track everything that happens to it, please". What am I missing about how using protocols to mediate all of my APIs is superior to mocks?

Why can't I specify what sorts of errors my function can throw? If I look at a framework's public API, for instance, I can easily see that a given function will throw errors, but when it comes to writing my error-handling code, I have to guess at which enum it will throw. I either have to run it and print it out and see myself (and hope there isn't something I'm missing), or I have to hope that it has accurate documentation.

That said, I'm happy for a lot of the good things we're getting in Swift, and am pretty happy with the language direction. It's just easier to complain about little things than praise the grand majority of things.

Axiem
Oct 19, 2005

I want to leave my mind blank, but I'm terrified of what will happen if I do

rjmccall posted:

Dumb reasons. We actually didn't immediately appreciate that there was value in considering methods from protocol extensions as possible implementations. Allowing this directly in protocols is the next obvious step, and we're working on it.

Cool.

quote:

Are you actually trying to react individually to every possible error that a function can throw? And if so, why? Generally in error-handling you at most want to call out a couple of special cases — which documentation is fine for — and then treat everything else uniformly. Trying to formalize exact error lists is really, really brittle from an API design perspective, causes a lot of downstream damage with wrapping errors, and comes with a ton of complexity and pedantry as a language feature.

Thinking about it, this does make sense. I was thinking more on the enum-level, but I guess my Stockholm Syndrome from Java is infecting me. I still don't necessarily trust the documentation, but I can't think of any really compelling example where I want to specially handle a particular error where I'm not going to know that such an error exists prior. At this point, my only possibly argument is "type safety!", but I don't think that's compelling enough.

So, cool.



I'm kind of curious what effect open-sourcing Swift will have on the annual cycle of big updates at WWDC. I suppose we'll find out (and at this point, the big updates might be done with).

Axiem
Oct 19, 2005

I want to leave my mind blank, but I'm terrified of what will happen if I do
I solved this sort of problem in my own code with generics:

code:
public typealias UnitName = String
public protocol Unit {
    static var name: UnitName { get }
}

public struct Quantity<U: Unit>: Comparable {
    public let value: UInt
    
    public init(_ value: UInt) {
        self.value = value
    }
}

public func < <U> (lhs: Quantity<U>, rhs: Quantity<U>) -> Bool {
    return lhs.value < rhs.value
}

public func == <U> (lhs: Quantity<U>, rhs: Quantity<U>) -> Bool {
    return lhs.value == rhs.value
}
And usage, from my tests:

code:
    struct Cochrane: Unit {
        static var name: UnitName { return "Cochrane" }
    }
    
    struct Tesla: Unit {
        static var name: UnitName { return "Tesla" }
    }

    func testCanBeCompared() {
        let one = Quantity<Cochrane>(42)
        let two = Quantity<Tesla>(42)
        let three = Quantity<Cochrane>(12)
        let four = Quantity<Tesla>(42)
        
        XCTAssertTrue(one > three)
        // XCTAssertFalse(one == two) // Compiler error
        XCTAssertTrue(two == four)
    }
It's worked out pretty well for me. Means I have to go and define all my custom relationships, but it gives me a nice level of type safety I wouldn't otherwise have, so I put up with the semi-boilerplate. Using typealiases once I have the Quantity<U> really cuts down on it, too.

Axiem
Oct 19, 2005

I want to leave my mind blank, but I'm terrified of what will happen if I do

Ender.uNF posted:

more stuff

This is really awesome! I didn't end up doing anything like that because it's not something I much need to do; my stuff is mostly multiplication and division (and I intentionally chose UInts instead of Doubles for my purposes; I could very much see doing it Doubles if it suits your needs)

Because my game has an NSTimer firing a tick every so often, I went in the direction of also capturing a DeltaQuantity, so e.g. my "income increase per tick" is a DeltaQuantity<Dollar>, to expressly capture the act of the quantity changing. For whatever reason, it just bothers me to be able to do + and - directly on quantities (what does it mean to do 3 meters - 5 meters? -2 meters? What does that even mean in terms of measuring (as opposed to position)).

There's a lot of cool ways someone could take this if so inclined.

Axiem
Oct 19, 2005

I want to leave my mind blank, but I'm terrified of what will happen if I do
The real solution is to replace methods that take selectors with methods that take functions. I was rather disappointed that iOS 9 didn't do that through more of UIKit.

Axiem
Oct 19, 2005

I want to leave my mind blank, but I'm terrified of what will happen if I do
Speaking of, when is Swift going to be open-sourced, again? I could have sworn it was sometime soon.

Axiem
Oct 19, 2005

I want to leave my mind blank, but I'm terrified of what will happen if I do

toiletbrush posted:

Is there any way in Swift to generate a copy of a struct but with one or more values changed?

I'm trying to write code without any var's, and having to write initializers to support this is the most awkward bit.

I handle this pretty similarly to the way rjmmccall mentioned:

code:
struct Star {
    let id: StarId
    let name: StarName
    let distance: Distance
    let rightAscension: RightAscension
    let declination: Declination
    let spectralClass: SpectralClass
    let magnitude: StarMagnitude

    init(id: StarId, name: String, distance: Distance, rightAscension: Angle, declination: Angle, spectralClass: SpectralClass, magnitude: Double) {
        self.id = id
        self.name = name
        self.distance = distance
        self.rightAscension = rightAscension
        self.declination = declination
        self.spectralClass = spectralClass
        self.magnitude = magnitude
    }

    init() {
        self.init(id: "star", name: "Star", distance: Distance(0), rightAscension: RightAscension(radians: 0), declination: Declination(radians: 0), spectralClass: SpectralClass(), magnitude: 1)
    }
    
    func withId(id: StarId) -> Star {
        return Star(id: id, name: name, distance: distance, rightAscension: rightAscension, declination: declination, spectralClass: spectralClass, magnitude: magnitude)
    }
    
    func withName(name: StarName) -> Star {
        return Star(id: id, name: name, distance: distance, rightAscension: rightAscension, declination: declination, spectralClass: spectralClass, magnitude: magnitude)
    }
    
    func withDistance(distance: Distance) -> Star {
        return Star(id: id, name: name, distance: distance, rightAscension: rightAscension, declination: declination, spectralClass: spectralClass, magnitude: magnitude)
    }
    
    func withRightAscension(rightAscension: RightAscension) -> Star {
        return Star(id: id, name: name, distance: distance, rightAscension: rightAscension, declination: declination, spectralClass: spectralClass, magnitude: magnitude)
    }
etc.

My primary annoyance is with the sheer amount of boilerplate, but it's mostly copy-pasting and swapping out which variable is being changed, otherwise, it works quite well, I think.

Axiem
Oct 19, 2005

I want to leave my mind blank, but I'm terrified of what will happen if I do

jawbroken posted:

If you're using structs in a reasonable way, and two lines is an issue, then you could define your own way of handling that. e.g.
code:
func with<T>(item: T, @noescape _ closure: inout T -> ()) -> T {
  var i = item
  closure(&i)
  return i
}

let p  = Point()
let p2 = with(b) { $0.x += 2; $0.y -= 2 }

The thing is, this breaks encapsulation; now whoever wants to create a struct with one variable different has to know how that variable is stored internal to the struct. I find that problematic.

Back to my star example:

code:
struct Star {
	// ...
    var location: Point { return Point(x: distance.ly * cos(rightAscension), y: distance.ly * sin(rightAscension)) }
    func withLocation(location: Point) -> Star {
        let radius = sqrt(Double(location.x * location.x) + Double(location.y * location.y))
        let theta: Angle = atan2(Double(location.y), x: Double(location.x))
        return Star(id: id, name: name, distance: Distance(radius), rightAscension: theta, declination: declination, spectralClass: spectralClass, magnitude: magnitude)
    }
}
Currently, I'm storing things about a star's location based on its polar coordinates (radius and angle), but I expose both polar and cartesian coordinates because each are useful in different ways, and I have the withX functions in order to let whoever's building one piecemeal to decide how to do it. But whoever calls information about the Star doesn't know how I'm internally storing that information. So if I change my mind later, the interface doesn't change, and no one calling changes.

If I went with the closure idea, then I'd have to expose how I'm storing that to whoever calls, and that seems to break encapsulation. It also means I'd probably have to do something fancy to allow setters on all three, or if I change my mind about internal storage later, I'd have to update all the callers.

As for why, for the Star, pretty much all of those are there for ease of testing; create an object with sane defaults and change the things I want in the test clearly. However, as you get higher up in my struct tree, I keep the same notation of withChangeToX to effectively cascade a couple of changes in the struct tree, and I like the consistency of naming.

Doing it with the withX functions also gives me the ability to pause anywhere in the chain and capture the struct, do some more work and capture that, and possibly run comparisons on the two, especially around computed values. The Star example is admittedly a little weak in that arena, because they're effectively static in my game.

Axiem
Oct 19, 2005

I want to leave my mind blank, but I'm terrified of what will happen if I do

jawbroken posted:

If I understand this correctly, you can do that anyway. Make a copy of the struct, directly perform whatever mutations you like, then compare the two. Everything will work correctly even if you are mutating structs that are properties of other structs. Nothing about your withX functions is special in that regard.

True; I just prefer to implicitly copy the struct rather than explicitly.

I understand the point you're making, especially since as value types structs already do the copies as necessary. It's mostly that something bothers me about letting an external object manipulate an instance of the struct by directly modifying the properties, instead of going through a function interface. That and my default is to always write "let" unless there's a compelling reason to write "var", which is probably an overreaction to seeing people make things mutable way too often.

Axiem
Oct 19, 2005

I want to leave my mind blank, but I'm terrified of what will happen if I do

Plorkyeran posted:

rjmccall can you confirm or deny that the wwdc surprise will be compiling swift to js so that we never have to use any other language?

Jesus Christ don't even joke about that. The hordes of js developers might hear you and actually attempt that monstrosity!

Axiem
Oct 19, 2005

I want to leave my mind blank, but I'm terrified of what will happen if I do

Toady posted:

I thought default public non-subclassability was sensible, but apparently bureaucratic forces are just restricting my freedom without reason or purpose. I'm grateful that people on the mailing list have opened my eyes.

For those of us who don't have the time to follow the mailing list, can someone elaborate on what's going on here?

Axiem
Oct 19, 2005

I want to leave my mind blank, but I'm terrified of what will happen if I do

rjmccall posted:

I linked the latest proposal.

That's what I get for leaving the thread open for several hours and replying without refreshing :downs:

Though I always did think the "subclass it and change it to break it further until it works again" was a bad pattern, so I think this is a good change. But I also tend to prefer using the lower-level frameworks whenever possible, instead of the higher-level frameworks that seem to need "fixing" more often.

Axiem
Oct 19, 2005

I want to leave my mind blank, but I'm terrified of what will happen if I do

Flobbster posted:

If the guy graduated from a 4-year CS program though, I can't excuse that.

As near as I can tell, the grand majority of 4-year CS programs don't bother to teach even rudimentary CS theory. It's a little depressing the number of colleagues who have CS degrees that I've had to explain what big-oh notation is to, or complexity classes. No, doing line-of-business applications isn't going to see that stuff very often, but it can be super important in analyzing and explaining why a particular piece of code isn't performing well. If colleges aren't teaching this, what are they teaching?

Axiem
Oct 19, 2005

I want to leave my mind blank, but I'm terrified of what will happen if I do
I am thrilled about the removal of C-style for loops. Not so much because I care about the for loops, but because I am really glad that the Swift team is willing to evaluate different programming language features and syntaxes on their own merits, rather than keeping historical baggage for the sake of history or something.

Axiem
Oct 19, 2005

I want to leave my mind blank, but I'm terrified of what will happen if I do
And preferably, your models know nothing about UIKit, and have interfaces that are value-based and easily tested independent of the VC/V. In practice, that can sometimes be hard.

Axiem
Oct 19, 2005

I want to leave my mind blank, but I'm terrified of what will happen if I do

uncle blog posted:

This seems really smart. Got any simple examples of it in practice?

Unfortunately, nothing in Swift that's public. At some point I want to write a blog post or something in my TIL for it, but I haven't yet. I might try to write it up soon; I'll try to remember to post it here when/if I do.

The best I can dig up was the example project for ESCObservable, which is written by a colleague of mine. He includes a "Presenter" object that has wiring tests; I would just wire in the VC and not write wiring tests, myself. And I use more closures and callbacks to handle it, but some of the ideas are still there.

Adbot
ADBOT LOVES YOU

Axiem
Oct 19, 2005

I want to leave my mind blank, but I'm terrified of what will happen if I do

Glimm posted:

I haven't looked into SPM in awhile, last I remember it didn't work with iOS at all

I...have missed that every time I've looked at it. That's an utter deal breaker. Are there plans to make SPM the sort of thing that can actually replace everything else? Carthage, after all, must be destroyed.

  • Locked thread