F#: Duck typing with generic constraints and why you will be speechless

So I’ve been working on a validation design that basically allows validation methods to be added to a list and then run. A small part of it:

    member x.Validate(valueToCheck:'a) =
      methodList 
      |> Seq.map (fun (methodToRun) -> methodToRun(valueToCheck)) 
      |> Seq.reduce (fun(outer) inner -> outer.MergeResults(inner))

What this is saying is take a list of methods, run them and make a list of their results, then merge the results into one. While I won’t get into what the results are (And I almost typed that as resluts which makes me think there is a refurbishing factory for sluts.) just know that each result can merge with another and combine values.

The biggest problem I was running into was a way to allow any method to be added and let the method decide what it needs to run correctly. In C# this can be done easily with dynamic:

  public static MethodResult ValidatePasswordExistence(dynamic modelToCheck)
  {
    var returnResult = new MethodResult(); 
    if (string.IsNullOrEmpty(modelToCheck.Password))
    {
      returnResult = returnResult.AddErrorMessage(UserError.PasswordIsNullOrEmpty);
    }

    return returnResult;
  }

The use of a dynamic parameter means that all validation methods can have the same signature which makes it really easy to send the same class into any method in the list. The problem with F# is there is no ready way to use dynamic and I’m not sure it should be. F# is heavily based on typing and dynamic kind of goes in the opposite direction. So I thought my ship was sunk, that is if I had a ship to sink and said ship hand’t sunk already.

Thanks to some help from the O, i was able to get past this not only without dynamic, but a way to strongly type any object coming into the method:

  let inline UserNameExists (userModel) =
    let userName = (^a : (member UserName : String with get) (userModel))

    let result =
      match userName with 
        | null -> (new MethodResult()).AddErrorMessage(UserErrors.UserNameIsNullOrEmpty)
        | "" -> (new MethodResult()).AddErrorMessage(UserErrors.UserNameIsNullOrEmpty)
        | _ -> new MethodResult()

    result

The important part is the second line. This line basically says that anything coming into the method as userModel has to have a property of UserName. One of the dangerous issues with dynamic is there are no compile time checks to make sure that any object going in will have the property or method you need it to. This isn’t true of F#. You not only get to pass in any object you want (That has what is required by the check) but you get complile time errros if you try to pass in an object that doesn’t fit the requirements.

Yup, that’s right. You’re speechless.

One thought on “F#: Duck typing with generic constraints and why you will be speechless”

Comments are closed.