F # generic constraint union type

I have been working with F # for several months, but have not found a satisfactory solution for my problem. I would like to describe the sequence of operations as a discriminated union of values ​​or operations on these values. So my type Val <'o> is defined as follows:

type Val<'o> = | Val of 'o | Func1 of ('a->'o) * Val<'a> | Func2 of ('a->'b->'o) * Val<'a> * Val<'b> 

The type Val <'o> can be converted to the type "o", recursively applying all the operations and saving the list of operations.

But I cannot define the generic types' a and 'b and their limitations unless I use Val <' a, 'b,' o>. If I do this, I have to define the generic Sub-Val types that I want to keep generic:

 type Val<'a, 'b, 'o> = | Val of 'o | Func1 of ('a->'o) * Val<?, ?, 'a> | Func2 of ('a->'b->'o) * Val<?, ?, 'a> * Val<?, ?, 'b> 

Is there any F # structure that can be adapted for this problem?

Thank you very much

[edit]

To describe my problem further, I am trying to get a comprehensive view of the FRP structure (but the generality problem is the same for events / signals, which are for values).
The view can be serialized to store the database, translated into text to display and edit the user, or evaluated to obtain the result:

 "Func (x -> x²) (Val(3.4))" <--> representation <--> 11.56 | user 

I made a prototype that works well using the union type of PrimitiveValue and the functions compiled at runtime into the common functions obj[] -> obj , but the evaluation is very difficult for type checking and casting (especially because I also use arrays and options in PrimitiveValue ), so I was looking for a more elegant and strongly typed solution.

+5
source share
1 answer

The main problem is that F # does not allow you to say that 'a and 'b in the case of discriminatory combining are “parameters” of the data stored in this case. Some other languages ​​support this (it is called generalized algebraic data types in Haskell), but there is the usual compromise that makes the language more complex.

You can actually emulate this in F #, but it is ugly - so I would think twice before going this route. The idea is that you can define an interface using a generic method that is called with appropriate arguments like 'a and 'b .

 type Val<'T> = | Val of 'T | Func of IFunc<'T> and IFunc<'T> = abstract Invoke<'R> : IFuncOperation<'T, 'R> -> 'R and IFuncOperation<'T2, 'R> = abstract Invoke<'T1> : ('T1 -> 'T2) * Val<'T1> -> 'R 

The value enclosed in Func can be specified by IFuncOperation and will call it with your 'a , which is an argument of the type of the general method - 'T1 in my name.

You can make the construction of values ​​reasonably enjoyable:

 let makeFunc fv = Func({ new IFunc<_> with member x.Invoke(op) = op.Invoke(f, v) }) let makeVal v = Val(v) let valString = makeFunc (fun n -> sprintf "Got: %d" n) (makeVal 42) 

valString now represents the int -> string transformation applied to Val<int> .

The code you need to write for pattern matching on Func is pretty ugly though:

 let rec eval<'T> (value:Val<'T>) : 'T = match value with | Val r -> r | Func f -> { new IFuncOperation<'T, 'T> with member x.Invoke<'S>(f, value:Val<'S>) = f (eval<'S> value) } |> f.Invoke eval valString 

I used a similar pattern in some internal units of Deedle, but never in code that was even close to what end users would write. I think this is acceptable on a very well hidden inner level, but I would definitely not use it in what was often called.

Depending on your original problem, perhaps a more pleasant way - you can define a discriminated union of PrimitiveValue to store various primitive values ​​that your calculation can do, or simply present operations using the interface - but it's hard to say which is better in your case, not knowing the context.

+6
source

All Articles