Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Repeatable pattern matching

Consider the following simple example.

type PaymentInstrument =
    | Check of string
    | CreditCard of string * DateTime

let printInstrumentName instrument = 
    match instrument with 
    | Check number-> printfn "check"
    | CreditCard (number, expirationDate) -> printfn "card"

let printRequisites instrument =
    match instrument with 
    | Check number -> printfn "check %s" number
    | CreditCard (number, expirationDate) -> printfn "card %s %A" number expirationDate

As you can see the same pattern matching logic is repeated in two functions. If I would use OOP I would create interface IPaymentInstrument, define two operations:

PrintInstrumentName and PrintRequisites

and then implement classes - one per payment instrument. To instantiate instrument depending on some external conditions I would use (for example) the factory pattern (PaymentInstrumentFactory).

If I would need to add a new payment instrument, I just need to add a new class which implements IPaymentInstrument interface and update factory instantiating logic. Other code that uses these classes remains as is.

But if I use the functional approach I should update each function where pattern matching on this type exists.

If there will be a lot of functions using PaymentInstrument type that will be a problem.

How to eliminate this problem using functional approach?

like image 267
eternity Avatar asked Oct 23 '15 08:10

eternity


People also ask

What is a repeatable pattern?

Repeating patterns are patterns where a group of elements repeat themselves as the pattern extends. Some examples of repeating patterns are: ABABABAB… AABAABAABAABAAB… ABCCABCC… As we consider these patterns we need to present them in a variety of different modes.

How do I find repetitions in regex?

A repeat is an expression that is repeated an arbitrary number of times. An expression followed by '*' can be repeated any number of times, including zero. An expression followed by '+' can be repeated any number of times, but at least once.

What is regex pattern matching?

A regular expression is a pattern of text that consists of ordinary characters, for example, letters a through z, and special characters. Character(s) Matches in searched string.

Why * is used in regex?

* - means "0 or more instances of the preceding regex token"


1 Answers

As Patryk Ćwiek points out in the comment above, you're encountering the Expression Problem, so you'll have to choose one or the other.

If the ability to add more data types is more important to you than the ability to easily add more behaviour, then an interface-based approach may be more appropriate.

In F#, you can still define object-oriented interfaces:

type IPaymentInstrument =
    abstract member PrintInstrumentName : unit -> unit
    abstract member PrintRequisites : unit -> unit

You can also create classes that implement this interface. Here's Check, and I'll leave CreditCard as an exercise to the reader:

type Check(number : string) =
    interface IPaymentInstrument with
        member this.PrintInstrumentName () = printfn "check"
        member this.PrintRequisites () = printfn "check %s" number

Yet, if you want to go the object-oriented way, you should begin to consider the SOLID principles, one of which is the Interface Segregation Principle (ISP). Once you start applying the ISP aggressively, you'll ultimately end up with interfaces with a single member, like this:

type IPaymentInstrumentNamePrinter =
    abstract member PrintInstrumentName : unit -> unit

type IPaymentInstrumentRequisitePrinter =
    abstract member PrintRequisites : unit -> unit

You can still implement this in classes:

type Check2(number : string) =
    interface IPaymentInstrumentNamePrinter with
        member this.PrintInstrumentName () = printfn "check"
    interface IPaymentInstrumentRequisitePrinter with
        member this.PrintRequisites () = printfn "check %s" number

This is beginning to seem slightly ridiculous now. If you're using F#, then why go to all the trouble of defining an interface with a single member?

Why not, instead, use functions?

Both desired interface members have the type unit -> unit (not a particularly 'functionally' looking type, though), so why not pass such functions around, and dispense with the interface overhead?

With the printInstrumentName and printRequisites functions from the OP, you already have the desired behaviour. If you want to turn them into polymorphic 'objects' that 'implement' the desired interface, you can close over them:

let myCheck = Check "1234"
let myNamePrinter () = printInstrumentName myCheck

In Functional Programming, we don't call these things objects, but rather closures. Instead of being data with behaviour, they're behaviour with data.

like image 120
Mark Seemann Avatar answered Oct 26 '22 09:10

Mark Seemann