I’ve been learning Scala lately, and it turns scala union types are sometimes done the same way I discovered for C#.
For reference, F# has union types that represent a set of strict alternatives.
1
2
3
4
| type PaymentTypes =
| CreditCard of CardNumber * SecurityCode * Expiration * NameOnCard
| ACH of (AccountNumber * RoutingNumber)
| Paypal of IntentToken
|
A value can be any single value of the alternatives and then unpacked using pattern matching.
1
2
3
4
5
| let ProcessPayment paymentInfo =
match paymentInfo with
| CreditCard cardInfo -> handleCard (cardInfo)
| ACH checkInfo -> handleCheck (checkInfo)
| Paypal paypalInfo -> handlePaypal (paypalInfo)
|
I previously discovered I could mimic F# union behavior
concisely in C# using the new-ish C# features for record types and pattern matching.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| record PaymentType{
public record CreditCard(CardNumber CardNumber, SecurityCode CVV, Expiration ExpirationDate, NameOnCard Name) : PaymentType();
public record ACH(AccountNumber AccountNumber, RoutingNumber RoutingNumber) : PaymentType();
public record Paypal(IntentToken Token) : PaymentType();
private PaymentType(){} // private constructor can prevent derived cases from being defined elsewhere
}
public void HandlePayment(PaymentType paymentInfo){
paymentInfo switch {
CreditCard cardInfo => //...
ACH checkInfo => //...
Paypal paypalInfo => //...
};
}
|
It turns out a similar approach is used in Scala. This tutorial
linked from the official Scala site demonstrates a union type as follows.
1
2
3
4
5
6
7
8
9
| sealed trait Symbol
case class Note(name: String, duration: String, octave: Int) extends Symbol
case class Rest(duration: String) extends Symbol
def symbolDuration(symbol: Symbol): String =
symbol match {
case Note(name, duration, octave) => duration
case Rest(duration) => duration
}
|
The core of this Scala example is deriving concrete types from an abstract type that can only be inherited within a limited scope.
This is essentially the same as my C# solution. Scala, however, provides full pattern matching exhaustiveness support and destructuring.
I’m not sure if Scala changed syntax, but I noticed another example that accomplished
the same using only an enum. The enum-based syntax is closer the F# syntax.
1
2
3
4
5
6
7
8
9
| enum Payment:
case Card(name: String, digits: Long, expires: Date)
case PayPal(email: String)
def process(kind: Payment) = kind match
case Card(name, digits, expires) =>
s"Processing credit card $name, $digits, $expires"
case PayPal(email) =>
s"Processing PayPal account $email"
|
This is facinating and a bit validating. I’m glad my C# approach is good enough that Scala teaches it.
Perhaps all C# needs to complete it’s union type experience is better static analyzer support.