Welcome back. In earlier lessons, you learned how protocols enable polymorphism by dispatching on data types, and you also defined behaviours to enforce module contracts. In this lesson, you will put the protocol side of that knowledge into action to build a small, extensible validation system. We will create a protocol, implement it for multiple structs, and run a unified workflow that works across different data types without conditionals.
Explanation:
defprotocol Validatordefines a single function,valid?/1. Each data type can supply its own implementation.EmailandPhoneNumberare simple structs. Protocol dispatch will use the struct type at runtime.- The
Emailimplementation checks for an “@” and a minimum length. - The
PhoneNumberimplementation enforces 10 digits using a regex. - Pattern matching in the function heads pulls the needed fields cleanly.
Note: Protocol dispatch is resolved efficiently after compilation (protocol consolidation). You get dynamic behavior with compile-time optimization.
Explanation:
validate_contacts/1iterates over mixed data, callsValidator.valid?/1, and pairs each item with its validation result.- The function does not need to know the concrete type. That is the core benefit of polymorphism.
Explanation:
- You build a mixed list of
EmailandPhoneNumberstructs. ContactValidator.validate_contacts/1triggers protocol dispatch for each item.- The output shows each contact with a true/false result, proving that one call site can handle multiple types without
casestatements.
Note: This design follows the open/closed principle. To support a new contact type (e.g., Address), define a struct and a defimpl Validator, for: Address, then everything else “just works.”
You created a protocol, implemented it for two structs, and wired a small workflow that validates mixed data without branching. This is a practical pattern for building flexible systems: keep call sites simple, push type-specific logic into protocol implementations, and add new types by extension, not modification.
