- Objective-C is not dead, it’s awesome, it’s a lovely language but you just won’t find a job if stick to it.
- Daniel misses the Objective-C’s
*
in Swift. Because that way he knows that something is (or isn’t) a reference type. He also misses header files because those are nice overviews of what he can call in a certain class. Otherwise, he loves Swift. - Context is important in code. If you would want to get
timeIntervalUntilNow
you need the inverse oftimeIntervalSinceNow
because the latter would return a negative number. Just putting a-
in front of it somewhere is bad, because there’s no context. Wrapping it in a method,-
is okay to put there. That provides context. SotimeIntervalUntilNow
would just return-timeIntervalSinceNow
. - Translating things from Objective-C to Swift is not thinking in Swift. Thinking in Swift doesn’t mean monads, map, flatmap, reduce and all the haskell stuff.
- If you use protocols, like
SequenceType
you get a lot for free on certain models. If you would have a model with your app sales and you make yourAppSales
struct conform toSequenceType
you could easily iterate through your numbers, you could use amap
function to calculate revenue and more. - A revenue calculation could be
Double(dailySales) * 0.99 * 0.70
but that looks pretty abstract. Cleaner code would be:Double(dailySales) * unitPrice * sellersPercentage
. And pulling that out from a map function and putting the calc into a function you could write something like:appSales.map(appRevenueForCopies)
. Filtering out anything < 0 like thisappSales.map{$0 > 0 ? $0 : 0}.map(appRevenueForCopies)
. But that’s not very readable. Better code:appSales.map(negativeToZero).map(appRevenueForCopies)
. - The
appRevenueForCopies
function returns aDouble
. But it’s not obvious what that double is. You createtypealias USDollars = Double
. Rounding the revenue in a function could result in something like:toTheNearestPenny(appRevenueForCopies(copiesSold))
. But that’s backwards, you read it inside out. - Doing
infix >> {associativity left}
helps. Function body is like this.
func >> <T, U>(input:T, transform: T->U) -> U {
return transform(input)
}
toTheNearestPenny(appRevenueForCopies(copiesSold))
can now be written as numberOfCopies >> revenueForCopies >> toTheNearestPenny
.
appSales.map{$0 > 0 ? $0 : 0}.map(appRevenueForCopies)
isn’t very pretty. Pulling that apart to two functions is better.func replaceNegativeWithZero()
,func arrayOfSales()
andfunc calcRevenueFromSales()
. With the new infix:appSales >> arrayOfSales >> replaceNegativeWithZero >> calcRevenueFromSales
. This is very readable. Maybe not super transparent but you abstract away all the cruft. If you want to know more you can zoom in on each component of this.- But.. custom operators. Can we do it without? Yes. If we extend
Int
we can put a private method on anInt
extension that returns a double. We can do the same with rounding to the nearest penny on Double. And then extendingInt
again we canreturn revenueForCopiesSold().roundedToTheNearestPenny()
. Resulting in code like7.revenueForCopiesSold()
. - Reduce is map, flatmap and filter. All at once. Try to implement map, flatmap and filter using a reduce call and generics. It’s not efficient or a good idea per se but it looks very interesting. Performance for this implementation is not good.
- Transducers can help with this. There’s no formal implementation in Swift but there’s examples if you look for them. A transducer takes a function from S to T (
f: S -> T
).[Copies]->[USDollars] = T
.[Int]->[USDollars] = S
sof: S -> T
is actuallyf: Int -> USDollars
. I really didn’t understand this last part.. it was so complex and amazing.