1

Suppose I have 2 record types

type A = { a: string; parameters: parameter list }
type B = { b: string; parameters: parameter list }

where type parameter = { name: string; value : string }

How can I write function parameter

let parameter name value entity = 
     { entity with parameters = List.append 
                                    parameters 
                                    [ { name = name; value = value; } ]
     }

Such as

let a =  { a = "a", parameters = [] } |> parameter "p", "v" // a is a record of type A
let b =  { b = "b", parameters = [] } |> parameter "p", "v" // b is record of type B
mbergal
  • 635
  • 6
  • 13
  • 1
    This is a dupe. Standard solution is to wrap A and B with a DU – John Palmer Mar 02 '16 at 02:56
  • 1
    See for example http://stackoverflow.com/questions/4986461/how-can-i-have-a-function-that-returns-different-types-in-f – John Palmer Mar 02 '16 at 02:56
  • Inside `parameter` do you only access `parameters` or do you need `a` and `b` as well? – CaringDev Mar 02 '16 at 04:53
  • You can implement the function like this: `let parameter name value entity = entity`. This meets the requirements, but is probably not what you want. What is it that you really want to do? – Mark Seemann Mar 02 '16 at 06:21
  • @MarkSeemann Sorry, should have made it clearer: let parameter name value entity = { entity with parameters = List.append parameters [ { name = name; value = value; } ] – mbergal Mar 03 '16 at 02:54
  • @JohnPalmer A and B are not related (besides having parameter list), I am not sure I can have them in one DU. For example, A and C might have a options list (similar to parameters), so A will have parameters and options, B will have just parameters and C will have just options – mbergal Mar 03 '16 at 03:02
  • @CaringDev Sorry, I've updated my question – mbergal Mar 03 '16 at 03:03
  • What is the type of the expression `{ name = name; value = value; }`? – Mark Seemann Mar 03 '16 at 05:37
  • @MarkSeemann I've updated code in question. – mbergal Mar 03 '16 at 06:42
  • @mbergal See also http://stackoverflow.com/questions/35038317/generic-method-on-record/35041429#35041429 – CaringDev Mar 03 '16 at 09:00

1 Answers1

1

It is not idiomatic F#, but this can be done using SRTP. I assume you have simplified the use-case for StackOverflow, but if A and B are really not related types, then I think you should revisit your overall program design.

I defined a Parameter type as this:

type Parameter =
  {
    Name : string
    Value : string
  }

Now, we need to add a method to types A and B that implement the addition of a parameter:

type A =
  {
    A : string
    Parameters : Parameter list
  }
  with
    member this.AddParameter(p : Parameter) =
      {
        this with
          Parameters =
            p :: this.Parameters
      }

And...

type B =
  {
    B : string
    Parameters : Parameter list
  }
  with
    member this.AddParameter(p : Parameter) =
      {
        this with
          Parameters =
            p :: this.Parameters
      }

Then we can write an inline function that calls this method:

let inline addParameter (p : Parameter) (x : ^t) : ^t =
  (^t : (member AddParameter : Parameter -> ^t) (x, p))

Here ^t will be replaced with A or B (or whatever) depending on the call-site. The syntax for SRTP isn't great, but it is better in F# 7.

Usage:

let p = { Name = "p"; Value = "abc" }

let a : A = 
  { A = "a"; Parameters = [] } 
  |> addParameter p

printfn $"%A{a}"

let b : B = 
  { B = "b"; Parameters = [] } 
  |> addParameter p

printfn $"%A{b}"
sdgfsdh
  • 33,689
  • 26
  • 132
  • 245