I'd like to create a function inc
that works as follows:
type operation = MulNeg | Add n | Sub n
let ops = [Add 3; Add 4; Sub 2; MulNeg] in
let inc l = ... in
inc ops (* should return [Add 4; Add 5; Sub 3; MulNeg] *)
I know I can implement it like this:
let inc l = List.map (function
| MulNeg -> MulNeg
| Add a -> Add (a + 1)
| Sub a -> Sub (a + 1)
) l
However in my program there are many more operations, therefore my goal would be to have a function that acts according to the number of arguments that the constructor has. The closest I've gotten to is this:
let inc l = List.map (function
| op a -> op (a + 1)
| op -> op
) l
However that raises an error on the op a
. Is there a legal way to make such a pattern ?
You can not pattern match on the number of constructor parameters. However, a common solution to your problem would be to introduce an extra indirection, e.g.,
type binop = Add | Sub | Mul | Div
type unop = Inc | Dec | Ref
type exp =
| Const of int
| Var of string
| Unop of unop * exp
| Binop of binop * exp * exp
let rec incr = function
| Const x -> Const (x+1)
| Var _ as v -> v
| Binop (op,x,y) -> Binop (op,incr x, incr y)
| Unop (op,x) -> Unop (op, incr x)
This won't scale, of course, when you have constructors with an arbitrary number of arguments. Then you either need to change the representation or stick to some other abstraction. For example, a Lisp-style language could be encoded as follows,
type prim = Add | Sub | Mul | Div
type op = Fun of string | Prim of prim
type exp =
| App of op * exp list
| Atom of string
If both solutions do not suit you, then you shouldn't use algebraic data types, and instead, stick to the visitor pattern.