Day 19 - Conditional data
Skills: 4
Pre-reading: 6.1.1.2, 6.1.2.3, 6.1.3.2, 6.1.3.3
Supplementary Videos
No video for this day. Please rely on the readings.
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
datamechanism: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) - As long as we know exactly which variant of the data type we have, we can use the dot notation, like with structured data, to access one of the variant-specific fields. For example:
payment-2.expiryorpayment-3.check-number - However, given a
PaymentMethodfor which we don't know the variant we have, we can't use the dot notation for field lookup. 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 expiring on " + exp
| checking(acc, rout, num) => "Paid by check from account " + acc
end
end - We've actually seen this type of data before -- the built-in
Optiontype, which we saw in Day 12, is essentially defined in Pyret as:
data Option:
| some(v)
| none
end
Class Exercises (40 mins)
- Define a
Vehicledata type with three variants:bike(no fields),car(withmakeandyearfields), andtruck(withmake,year, andcapacityfields). Create one example of each variant. - Write a function
vehicle-agethat takes aVehicleand the current year, and returns the age in years. For bikes, return 0. - Define a
Gradedata 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-gpathat converts anyGradeto 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-summarythat takes aPaymentMethodand returns a string: "Cash payment", "Card ending in XXXX" (last 4 digits), or "Check #NNNN" (check number). - Define a
WeatherReportwith variants:sunny(temperature only),rainy(temperature and precipitation amount), andsnowy(temperature, precipitation, and wind speed). Write a functionis-severethat returnstrueif: 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
casesto process conditional data, handling each possible variant. - Different variants can have different numbers and types of fields.