神刀安全网

Swift: Money with Phantom Types 👻

I’ve seen Phantom Types before, including in this objc.io article about Phantom Types and in conference talks, but while I like the idea of Phantom Types and think they’re cool and interesting, I haven’t actually used them ever in my own code. Mostly, because it’s not yet natural for me to see a problem and think “Ah, Phantom Types would be the perfect solution!”.

However, I would like to have Phantom Types more accessible for myself in the future and I really enjoyed the example that @johannesweiss gave in his talk  The Type System is Your Friend , so I’m going to write his example out here to be more searchable / discoverable for myself and hopefully others!

The problem that @johannesweiss with Phantom Types is currency conversion :money_with_wings:��:money_with_wings: !

While in the video, @johannesweiss goes straight into the Phantom Type solution, I wanted to first try a solution without the Phantom Types to see the before and after!

Without Phantom Types :scream:

I would probably have a Money struct that includes both the amount and the currency of the amount:

struct Money {     enum Currency {         case GBP, EUR, USD     }          let amount: NSDecimalNumber     let currency: Currency } 

But let’s say I want to convert the money to a different currency – let’s say from GBP to EUR:

enum ConversionResult {     case Success(Money)     case Error }   func convertGBPtoEUR(gbp: Money) -> ConversionResult {     if gbp.currency == .GBP {         let forex = NSDecimalNumber(mantissa: 133263, exponent: -5, isNegative: false)         let convertedAmount = gbp.amount.decimalNumberByMultiplyingBy(forex)         return .Success(Money(amount: convertedAmount, currency: .EUR))     } else {         return .Error     } }   // Usage let fivePounds = Money(amount: 5, currency: .GBP) let threeEuros = Money(amount: 3, currency: .EUR)   let conversionResult = convertGBPtoEUR(fivePounds) switch conversionResult { case .Success(let money):     print("Yay! Money!!! /(money)") // "Yay! Money!!! Money(amount: 6.66315, currency: Money.Currency.EUR)/n" case .Error:     print("Error!!! Bad money!!!") }   // passing in the wrong currency is allowed here! let badConversionResult = convertGBPtoEUR(threeEuros) switch badConversionResult { case .Success(let money):     print("Yay! Money!!! /(money)") case .Error:     print("Error!!! Bad money!!!") // "Error!!! Bad money!!!/n" } 

We can immediately see the problem. I have to check to make sure that the money passed into my function is of the correct currency. And if it’s not, I have to return an Error. Which cascades down and now the function that calls this function has to handle an error case!

With Phantom Types :ghost:

Phantom Types are Types that don’t have any functionality. According to @johannesweiss:

A phantom type is a parameterized type whose type parameters do not all appear in its definition. I have what you could call a useless type parameter C . This is the Currency , and it is not even used in the actual properties of the struct. To fill Money , I created an empty protocol, Currency, so that I may mark any type as a currency.

Like this:

protocol Currency { }   enum GBP:Currency { } enum EUR:Currency { } enum USD:Currency { }   struct Money<C: Currency> {     let amount: NSDecimalNumber } 

Now the GBP -> EUR conversion function looks like this!

// the method signature is super nice now! // inputing Money in GBP, return Money in EUR! func convertGBPtoEUR(gbp: Money<GBP>) -> Money<EUR> {     let forex = NSDecimalNumber(mantissa: 133263, exponent: -5, isNegative: false)     let convertedAmount = gbp.amount.decimalNumberByMultiplyingBy(forex)     return Money(amount: convertedAmount) }   // Usage let fivePounds = Money<GBP>(amount: 5) let threeEuros = Money<EUR>(amount: 3)   let convertedMoney = convertGBPtoEUR(fivePounds) // success! let badMoney = convertGBPtoEUR(threeEuros) // compiler error! 

Notice that we no longer need to manually check for correct currency type or return any errors. The compiler does the work for us!

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » Swift: Money with Phantom Types 👻

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
分享按钮