It seems like F# code often pattern matches against types. Certainly
match opt with
| Some val -> Something(val)
| None -> Different()
seems common.
But from an OOP perspective, that looks an awful lot like control-flow based on a runtime type check, which would typically be frowned on. To spell it out, in OOP you'd probably prefer to use overloading:
type T =
abstract member Route : unit -> unit
type Foo() =
interface T with
member this.Route() = printfn "Go left"
type Bar() =
interface T with
member this.Route() = printfn "Go right"
This is certainly more code. OTOH, it seems to my OOP-y mind to have structural advantages:
- extension to a new form of
T
is easy; - I don't have to worry about finding duplication of the route-choosing control flow; and
- route choice is immutable in the sense that once I have a
Foo
in hand, I need never worry aboutBar.Route()
's implementation
Are there advantages to pattern-matching against types that I'm not seeing? Is it considered idiomatic or is it a capability that is not commonly used?
But from an OOP perspective, that looks an awful lot like control-flow based on a runtime type check, which would typically be frowned on.
-- sounds too dogmatic. Sometimes, you want to separate your ops from your hierarchy: maybe 1) you can't add an op to a hierarchy b/c you don't own the hierarchy; 2) the classes you want to have the op don't match your hierarchy; 3) you could add the op to your hierarchy, but don't want to b/c you don't want to clutter your hierarchy's API with a bunch of crap that most clients don't use.Some
andNone
aren't types. They're both constructors whose types areforall a. a -> option a
andforall a. option a
(sorry, not sure what the syntax for type annotations is in F#).