Search code examples
f#abstractiondestructuring

How to abstract over a destructuring in F#


I have some code repetition that I really want to get rid of -

// here's some lib code the repetitive code relies on...

module Option

let definitize opts = List.choose id opts

// here's the start of another file...

module Ast

type Expr =
| Violation of Expr
| Boolean of bool
| String of string

// here's the repetitive code...

let exprToOptViolation expr = match expr with | Violation v -> Some v | _ -> None
let exprToOptBoolean expr = match expr with | Boolean b -> Some b | _ -> None
let exprToOptStr expr = match expr with | String n -> Some n | _ -> None

let exprsToOptViolationStrs exprs = List.map exprToOptViolation exprs
let exprsToOptBools exprs = List.map exprToOptBoolean exprs
let exprsToOptStrs exprs = List.map exprToOptStr exprs

let exprsToViolationStrs exprs =
    let optViolationStrs = exprsToOptViolationStrs exprs
    let violationStrs = Option.definitize optViolationStrs
    (optViolationStrs, violationStrs)    

let exprsToBools exprs =
    let optBools = exprsToOptBools exprs
    let bools = Option.definitize optBools
    (optBools, bools)

let exprsToStrs exprs =
    let optStrs = exprsToOptStrs exprs
    let strs = Option.definitize optStrs
    (optStrs, strs)

As you can see, it is the same algorithm repeated 3 times. However, I don't know how to generalize code that requires passing a destructuring facility like match expr with | destructureFn a -> Some a | _ -> None. Can someone help? I've actually got 5 repetitions in my code (and growing) that need to get factored down.

Cheers!

* CONCLUSION *

Using desco's answer, I've reached this refactoring -

let exprsToValues exprToOptValue exprs =
    let optValues = List.map exprToOptValue exprs
    let values = Option.definitize optValues
    (optValues, values)

let exprsToViolationStrs exprs = exprsToValues (fun expr -> match expr with | Violation v -> Some v | _ -> None) exprs
let exprsToBools exprs = exprsToValues (fun expr -> match expr with | Boolean b -> Some b | _ -> None) exprs
let exprsToStrs exprs = exprsToValues (fun expr -> match expr with | String s -> Some s | _ -> None) exprs

Thanks desco!


Solution

  • it is pretty complicated to reason without seeing actual code, probably something like this will work

    type E = | Violation of string | Boolean of bool | String of string
    module Option = 
        let definitize vOpt = vOpt |> List.map (function Some x -> sprintf "%A" x | _ -> "none")
    
    let mkEToViolationsStr f exprs = 
        let optViolationStrs = List.map f exprs
        let violationStrs = Option.definitize optViolationStrs
        (optViolationStrs, violationStrs)    
    
    let exprsToViolationStrs2 = mkEToViolationsStr (function Violation v -> Some v | _ -> None)
    let exprsToBools2 = mkEToViolationsStr (function Boolean b -> Some b | _ -> None)
    let exprsToStrs2 = mkEToViolationsStr (function String s -> Some s | _ -> None)