Day 19 - Conditional data
Skills: 4
Pre-reading: 6.1.1.2, 6.1.2.3, 6.1.3.2, 6.1.3.3
Intro (15 mins)
- Sometimes, rather than data being composed of different parts, as we saw last time, data comes in multiple, incompatible, forms.
- For example, a payment system might need to track the source of payment, which
could be:
- Cash, with no further information
- Check, with the bank account number and routing number (and possibly more information -- account number, check number, etc)
- Credit Card, with the credit card number (and possibly more information -- expiration date, security code, etc)
- This doesn't fit into the pattern of structured data, since different alternatives have different, incompatible, components. A security code makes no sense for a check, and a check number has no bearing on a credit card, and all of the extra fields make no sense for cash.
- Instead, this type of data is conditional -- data that takes the shape of exactly one of several variants.
- We can define the above using the same
data
mechanism:data PaymentMethod:
| cash
| credit(card-number :: String, expiry :: String)
| checking(bank-account :: String, routing :: String, check-number :: Number)
end - Note that there is one data type
PaymentMethod
, but three ways of constructing it --cash
,credit
, andchecking
. - We create examples of this data similarly to how we did with structured data:
payment-1 = cash
payment-2 = credit("1111-2222-3333-4444", "09/26")
payment-3 = checking("987654321", "111", 55) - To use conditional data, however, we can't use the dot notation for field lookup, like with structured data,
since given a
PaymentMethod
, we don't know which variant we have! Instead, we use a new language feature calledcases
, which allows you to write down what to do in each possible case of the data, creating names for fields in the variants that have fields:fun display-payment(p :: PaymentMethod) -> String:
cases (PaymentMethod) p:
| cash => "Paid in cash"
| credit(cn, exp) => "Paid by credit card " + to-string(string-length(cn))
| checking(acc, rout, num) => "Paid by check from account " + acc
end
end - We've actually seen this type of data before -- the built-in
Option
type, which we saw in Day 12, is essentially defined in Pyret as:
data Option:
| some(v)
| none
end
Class Exercises (40 mins)
- Define a
Vehicle
data type with three variants:bike
(no fields),car
(withmake
andyear
fields), andtruck
(withmake
,year
, andcapacity
fields). Create one example of each variant. - Write a function
vehicle-age
that takes aVehicle
and the current year, and returns the age in years. For bikes, return 0. - Define a
Grade
data type with variants:letter
(with a string field for A/B/C/D/F),percent
(with a number field), andpass-fail
(with a boolean field). Create examples of each. - Write a function
grade-to-gpa
that converts anyGrade
to a 4.0 scale number. Use:A=4.0, B=3.0, C=2.0, D=1.0, F=0.0
; percentages:90+=4.0, 80-89=3.0, 70-79=2.0, 60-69=1.0, <60=0.0
; pass-fail:pass=4.0, fail=0.0
. - Write a function
payment-summary
that takes aPaymentMethod
and returns a string: "Cash payment", "Card ending in XXXX" (last 4 digits), or "Check #NNNN" (check number). - Define a
WeatherReport
with variants:sunny
(temperature only),rainy
(temperature and precipitation amount), andsnowy
(temperature, precipitation, and wind speed). Write a functionis-severe
that returnstrue
if: rainy with >2 inches, snowy with >30 mph wind, or sunny above 95°F.
Wrap-up (5 mins)
- Conditional data represents information that can take one of several different forms.
- Use
cases
to process conditional data, handling each possible variant. - Different variants can have different numbers and types of fields.