How to abstract over a destructuring in F#
-
15-03-2021 - |
Pregunta
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!
Solución
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)
Otros consejos
I may be wrong but optValues
in
let optValues = List.map exprToOptValue exprs
seems to be unnecessary. It will return Expr option
, string option
or bool option
in each case, which makes it more difficult to use. Either you keep exprs
and return it to do some calculation later:
let exprsToValues exprToOptValue exprs =
let values = List.choose exprToOptValue exprs
(exprs, values)
or simply returns values
only. In the latter case, meanings of exprsToSomething
functions totally fit to their names:
let exprsToViolationStrs exprs = List.choose (function | Violation v -> Some v | _ -> None) exprs
let exprsToBools exprs = List.choose (function | Boolean b -> Some b | _ -> None) exprs
let exprsToStrs exprs = List.choose (function | String s -> Some s | _ -> None) exprs