ljs
ljs

Reputation: 37817

Adjusting value of a discriminated union object

So, yet another discriminated union question :-)

Assume I have a discriminated union like so:-

type Foo =
  | A of string
  | B of int
  | C of char
  | D of string

I would like to be able to use a function, appendStringToFoo, as follows:-

let someVal = A("hi")
let anotherVal = D("yo")

let hiya = someVal |> appendStringToFoo "ya"
let yoyo = anotherVal |> appendStringToFoo "yo"

Where hiya = A("hiya") and yoyo = D("yoyo").

Obviously, I'd also go on to write separate functions appendIntToFoo, appendCharToFoo, etc.

So, effectively, a function similar to:-

let appendStringToFoo str fooValue =
  fooValue(fooValue.Value + str)

Which doesn't seem possible.

I do not want to do the following if I can avoid it:-

let appendStringToFoo str fooValue =
    match fooValue with
    | A(originalStr) -> A(originalStr + str)
    | D(originalStr) -> D(originalStr + str)

As that means I'd have to re-write this code every time I added a new union case.

Any ideas?

Upvotes: 1

Views: 274

Answers (1)

Brian
Brian

Reputation: 118895

You have to do the thing you don't want to do.

An alternative would be along the lines of

  • abstract class Foo
  • abstract class StringyFoo : Foo { void AppendString(string); string S; }
  • class A : StringyFoo
  • class D : StringyFoo
  • abstract class IntyFoo : Foo
  • class B : IntyFoo

which potentially avoids 'fixing up appendString() each time you add a new token type', but I think that for a lexer (where Foo=Token) you'll be happier overall with the DU.

In general with lexing/parsing, whether you use class hierarchies and the Visitor pattern, or you use algebraic data types, you always must write N*M code for N features and M subtypes; there's no avoiding it, so don't bother trying to find cute tricks to try to avoid it, it only leads to misery.

Upvotes: 2

Related Questions