DependentTypes icon indicating copy to clipboard operation
DependentTypes copied to clipboard

Add support for validation functions to have a single parameter

Open willnationsdev opened this issue 4 years ago • 2 comments

Because the dependent type depends on the new function of the validator being unit -> 'Pi, it is impossible to have the DependentType creation take in a value that should go to the validator function. In my use case, I have validation that always takes in some form of logger function. I specifically do this because each validation operation within a validator function is wrapped in code quotes and stringified with unquote before logging the error and exiting early with a None.

I wish this could be "fixed" by just implementing a LogSomeDependentType or some such, but apparently you can't pass any parameters to the new() of a type parameter.

willnationsdev avatar Jan 31 '21 07:01 willnationsdev

That's interesting. Thanks. If you fork, keep me up to date on what you do.

jackfoxy avatar Jan 31 '21 16:01 jackfoxy

Hey, just updating you. I did manage to get it to work.

Had to change Pi to look like this, with a log function parameter and writable property:

    [<Class>]
    /// Construction / validation type for DependentType 
    type Pi<'Config, 'T, 'T2> (logexn, config: 'Config, pi: (exn -> unit) -> 'Config -> 'T -> 'T2) =
        member this.Create x = pi this.Logger config x
        member val Logger : exn -> unit = logexn with get, set

Then modified TryCreate in SomeDependentType to use an empty new() for instantiation, but assign the logger afterward before moving on:

            /// Create instance of SomeDependentType option.
            /// If the 'T2 (base type) is option, lifts Some/None of base type element to DependentType option.
            static member TryCreate (logexn, x) : SomeDependentType<'Pi, 'Config, 'T, 'T2> option =
                let pi = new 'Pi()
                pi.Logger <- logexn
                match pi.Create x with
                | Some value ->
                    Some (SomeDependentType value)
                | None ->
                     None

The BaseString is a DependentType that must have trimmed non-null/whitespace strings with optional capacity limit and/or regex pattern conformity. The assertQuote function is a helper that ensures that the code quote is true and throws an exception otherwise. The log function is just an extra parameter for the Pi constructor before you give the config value (so it's required). Also, any validators you implement need to be given an empty constructor:

    module BaseStringDef =
        type BaseStringConfig = int option * string option
        let validateString logexn (config: BaseStringConfig) s =
            let capacity, regexPattern = config
            try
                assertQuote <@ s |> System.String.IsNullOrWhiteSpace |> not @>
                let s = s.Trim()
                if capacity.IsSome then assertQuote <@ s.Length <= capacity.Value @>
                if regexPattern.IsSome then assertQuote <@ Regex.IsMatch (s, regexPattern.Value) @>
                Some s
            with ex -> logexn ex; None

        module Validators =
            type BaseStringValidator(logexn, capacity, regexPattern) =
                inherit Pi<BaseStringConfig, string, string option>(logexn, (capacity, regexPattern), validateString)
                new() = BaseStringValidator(ignore, None, None)

It seems to be working well enough for now.

willnationsdev avatar Feb 01 '21 13:02 willnationsdev