F#泛型约束联合类型

时间:2016-07-11 17:25:29

标签: generics f# discriminated-union

我和F#一起工作了几个月,但没有找到任何令人满意的解决方案来解决我的问题。 我想将一系列操作描述为有价值的价值联合或对这些价值的操作。 这样,我的类型Val<> o>定义如下:

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

类型Val&lt;&gt; o&gt;可以通过递归应用所有操作转换为&o; o类型,并仍保留操作列表。

但如果我不使用Val&lt;&#39; a,&b; b,我就无法定义通用类型&#39; a和&b; b及其约束条件, &#39;○取代。 如果我这样做,我必须定义sub-Val泛型类型,我希望保持通用:

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

是否有适合此问题的F#结构?

非常感谢

[编辑]

为了进一步描述我的问题,我试图详尽地描述一个FRP结构(但是对于值的事件/信号,通用性问题是相同的。) 表示可以序列化用于数据库存储,翻译成文本以供显示,用户编辑或评估以获得结果:

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

我使用PrimitiveValue联合类型使原型工作得很好,并将在运行时编译的字符串函数化为通用obj[] -> obj函数,但对类型检查和转换的评估非常繁重(特别是因为我是也使用PrimitiveValue中的数组和选项,所以我一直在寻找一种更优雅,更强类型的解决方案。

1 个答案:

答案 0 :(得分:6)

这里的基本问题是F#不允许你说区分联合的情况下'a'b是存储在案例中的数据的“参数”。其他一些语言支持这一点(它在Haskell中称为广义代数数据类型),但通常需要权衡使语言更复杂。

你可以在F#中实际模仿这个,但它很难看 - 所以在走这条路之前我会三思而后行。我们的想法是,您可以使用通用方法定义一个接口,该方法可以使用适当的类型参数'a'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

Func中包含的值可以IFuncOperation,它会调用它,'a是泛型方法的类型参数 - 我的命名中的'T1

您可以合理地构建值:

let makeFunc f v =
  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表示应用于int -> string的{​​{1}}转换。

Val<int>上编写模式匹配所需的代码非常难看:

Func

我在Deedle的一些内部使用了类似的模式,但从未在代码中使用甚至接近最终用户所写的内容。我认为这在一些非常隐蔽的内部水平上是可以接受的,但我肯定会避免在经常被称为的东西中使用它。

根据原始问题的原因,可能有一种更好的方法 - 你可以定义一个有区别的联合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 来保存你的计算可以产生的各种原始值,或者你可以只使用一个来表示操作。界面 - 但是如果不了解背景,很难说你的情况会更好。