-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Discussion] Creating Generic Wrappers for Validated Values #6
Comments
I think I have it.
|
I would like to have access to the underlying 't as a property named "Value", but I can't yet see how to get this to work
...it may not be so important. |
First of all, this is really excellent work. Some comments/suggestions:
Other Solutions.NET IL cannot accept literals as type parameters. This is higher than an F# problem. Microsoft is understandably reticent to make changes to IL (they have the highest standards for backwards compatibility), especially with .NET Core going on now. I think IL is open-sourced somewhere. Maybe if someone demonstrates a change to accept integers and arrays as type parameters Microsoft may consider accepting the change, someday. The other thing I have thought about is a generative type provider. I believe it will accept literals, but up until now TPs do not accept other types. I have heard of people trying to solve this problem. |
Hey Jack, thanks for your comments.
Other Solutions) lets say we have this type SomeGeneric<'a,'b> = {a: 'a; b: 'b}
type FixedValues = SomeGeneric<100, "foo"> This could be easily expanded into type SomeGeneric<'a,'b> = {a: 'a; b: 'b}
type Validator_FixedValues_100() = inherit Validator<int, int>(100, validateSingletonValue)
type Validator_FixedValues_foo() = inherit Validator<string, string>("foo", validateSingletonValue)
type Value_100()= LimitedValue<Validator_FixedValues_100, int, int>
type Value_foo()= LimitedValue<Validator_FixedValues_foo, string, string>
type FixedValues = SomeGeneric<Value_100, Value_foo> on the fly |
I'm having fun playing with this.
If it did not require the hint I would advocate for the static member to be an overload of the TryParse (TryCreate) static member. Language suggestion and/or NuGet packageI hope others contribute comments soon. I think some more perspectives would be valuable before submitting a language suggestion and/or creating a NuGet package. I like your idea for using this to leverage static type parameters, but I haven't experimented with that yet. Not including static parameters (which should just work in the language suggestion, and I haven't thought about it for a package) this is my current take on structuring the namespace.
Dependent FunctionAs promised a dependent function.
|
Link to article https://robkuz.github.io/Limited-Values/ |
Hey Jack,
As for your suggestion
What can I do with this? Can you give some code examples? Another point is that |
Rob, I'm not familiar with Idris either, and now I'm trying to wrap my head around the utility of resolving dependent types at compile time. I think language professionals and enthusiasts think of dependent types more in solving formal methods problems, than solving day-to-day business problems. So my view is going to be controversial. I realize I'm misusing Cctor. I couldn't think of a better name. Anyway, I think your LimitedValue<'T> alone is by far the most useful and common case. I put forward the idea of <'T, 'T2> only for completeness of the theoretical dependent type. I can only make some contrived examples (see below). Also I want to point out my previous "dependent function" example is completely contrived. I can't think of a way it could be useful in any strongly typed language. My first guess at implementing <'T, 'T2> was too complicated. Contemplating your original work on LimitedValue I realized it only takes minor modifications to make it work as <'T, 'T2>. (To keep this issue thread readable I'm posting 2 more comments.) |
Modifications to LimitedValue to achieve a dependent type that takes 'T to 'T2.
|
Examples of dependent types taking 'T to 'T2. Perhaps my examples are not too contrived, and someone else could think of this being useful.
|
Hey Jack, thanks for the further input.
Nevertheless my preference would be to do this type transformation via some let a: Max10Int = (tryMake 9).Value
let b: Len2String = map (fun x -> sprintf "%i" x) a While I was pondering about this I became painfully aware about some serious downsides of the actual implementation. A functor needs to be structure preserving so a potential let map f (v:DependentType<'a,'b,'c,'d>) : DependentType<'w,'x,'y,'z> =
let (DependentType v') = v
let r = v' |> f |> DependentType
match r with
| Some x -> r //we can not return an Option here but must return a DependentType
| _ -> failwith "OOOO!" and then we can easily see this happen let a: Max10Int = (tryMake 9).Value
let b: Max10Int = map (fun x -> x + 2) a Bummer! So I think the only way to allow for a proper module DependentTypes2Fns =
let inline tryMake (x: ^S) : ^T =
(^T: (static member TryMake: ^S -> ^T) x)
let inline extract (x:^S) =
(^S: (static member Extract: ^S -> ^T) x)
let inline convertTo (x: ^S) : Option< ^T> =
(^T: (static member ConvertTo: ^S -> Option< ^T>) x)
let inline map f (x: ^S) : ^T =
(^S: (static member Map: ^S -> ^Q -> ^T) (x, f))
module DependentTypes2 =
open DependentTypes2Fns
type Validator<'Config, 'T> (config: 'Config, vfn: 'Config -> 'T -> Option<'T>) =
member __.TryMake(x:'T) : Option<'T> = vfn config x
type DependentType<'Validator, 'Config, 'T when 'Validator :> Validator<'Config, 'T>
and 'Validator : (new: unit -> 'Validator)> =
| DependentType of 'T
| Failure of string
with
member this.Value =
match this with
| DependentType v -> v
| _ -> failwith "This is a Failure"
static member TryMake(x:'T) : DependentType<'Validator, 'Config, 'T> =
match (new 'Validator()).TryMake x with
| Some v -> DependentType v
| _ -> Failure (sprintf "can not create this type %A" (typeof<DependentType<'Validator, 'Config, 'T>>))
static member Extract (x : DependentType<'Validator, 'Config, 'T> ) = x.Value
static member inline Map (x : DependentType<'a, 'b, 'c> , f : 'c -> 'q) : DependentType<'o, 'p, 'q> =
match x with
| DependentType v -> v |> f |> tryMake
| Failure v -> Failure v
static member inline ConvertTo(x : DependentType<'a, 'b, 'c> ) : DependentType<'o, 'p, 'q> = map id x
let validate normalize fn v =
if fn (normalize v) then Some (normalize v) else None
let validateLen len s =
validate id (fun (s:string) -> s.Length <= len) s
type LenValidator(config) =
inherit Validator<int, string>(config, validateLen)
type Size5 () = inherit LenValidator(5)
type String5 = DependentType<Size5, int, string>
let okString = String5.TryMake "cool" // DependentType
let failString = String5.TryMake "much too long" //Failure
let addExclamation x = sprintf "%A!" x
let a: String5 = map addExclamation okString // DependentType<_,_, "cool!">
let b: String5 = a |>> addExclamation // Failure |
Hey Rob, Failure is not an option. I hope you like my pun ;) In seriousness, as you noted, LimitedValue/DependentType is not a functor. Maybe I may have taken this thread off on too many tangents. I wanted to investigate corner and edge cases. I still want to apply this to a real library of mine (and I have one in mind) to reinforce my opinions, but as of now my thinking is:
|
Hi Rob, I had a very good experience converting some types in a real library to dependent types and then using those types productively.
Some lessons learned: In my cases I would like some more create overload methods. TryCreate/TryParse would suffice if users could add their own extension methods, however this does not seem possible. So I think all the useful overloads need to be part of the base library. This is a little messy, but there is precedent in the .NET Framework of some classes with many overloads. Extension methods would also let users be creative in other ways. Of course there are other ways for users to handle these cases by creating their own functions over TryCreate/TryParse. Added Dependent Types apparently preserve the equality and comparison behavior of the underlying type. (very nice) I could not find a way to use this technique for generic dependent types, e.g. NonEmptySet<'T>. (This does not matter much. I'm just remarking that this is so.) |
I'm very interested in this project, and probably have more feedback, but first I'm trying to get the "library" code building, and the LimitedValue type as printed does not build for me
I'm not sure exactly what you have in mind here.
The text was updated successfully, but these errors were encountered: