F# 判别并集的运算符重载

F# 判别并集的运算符重载,f#,operator-overloading,discriminated-union,F#,Operator Overloading,Discriminated Union,我正在尝试编写一些可以使用标量或向量的数字代码(在本例中,分别是DiffSharp的D和DV类型)。有时我希望能够使用其中一种,因此我为他们定义了一个歧视性联盟: type IBroadcastable = | Scalar of D | Vect of DV 对于这两种类型,很多运算符都已重载,因此要在IBroadcastable上使用它们,我将这样的代码添加到union中: static member Exp x = match x with |

我正在尝试编写一些可以使用标量或向量的数字代码(在本例中,分别是DiffSharp的D和DV类型)。有时我希望能够使用其中一种,因此我为他们定义了一个歧视性联盟:

type IBroadcastable =
    | Scalar of D
    | Vect of DV
对于这两种类型,很多运算符都已重载,因此要在
IBroadcastable
上使用它们,我将这样的代码添加到union中:

static member Exp x = 
    match x with
        | Scalar x -> Scalar (exp x)
        | Vect x -> Vect (exp x)
这似乎是多余的。我有没有办法在联合体上使用运算符而不必为其编写新的重载?或者我应该使用不同的模式(即不是歧视性的联盟)?我希望使用此类型的示例:

let ll (y: IBroadcastable) (theta: IBroadcastable) = y*theta-(exp theta)

*
-
将具有更复杂的行为(阵列广播),这需要我自己来描述,但是
exp
操作符很简单,如上所述。这需要是一个函数,因为我希望能够部分应用y参数,使用DiffSharp获得梯度,并使其相对于θ参数最大化。

基本上,因为您定义的是一个抽象,所以需要根据该抽象定义您的操作。这一成本必须通过它在代码的其他地方为您提供的便利来抵消

你可能想知道的是,F#是否会允许你在你的特定案例中使用样板。除了使用
函数
关键字之外,这并不是真的,因为两个分支都在做不同的事情:绑定变量
x
的类型不同,您将它们包装在不同的并集案例中。如果你真的在做同样的事情,你可以这样写,例如:

type DU =
| A of float * float
| B of float * string
with
    static member Exp = function
        | A (b, _)
        | B (b, _) -> exp b // only write the logic once

示例函数
ll
实际上更通用—它可以处理任何支持它使用的操作的东西,甚至不是
D
DV
的东西。如果使用
inline
定义该函数,则可以在以下两种情况下调用该函数:

let inline ll y theta = y*theta-(exp theta)
inline
修饰符允许F#使用静态成员约束,调用函数时所需的成员可以满足静态成员约束(与必须使用.NET运行时提供的内容编译的普通泛型函数不同)

我希望这不会适用于所有代码,因为您将需要一些特定于
D
DV
的操作,但没有通用的F#函数,例如
exp
。实际上,您可以使用静态成员约束访问这些约束,尽管这有点麻烦

假设
D
DV
值都有一个返回
string
的成员
Foo
,您可以编写:

let inline foo (x:^T) = 
  (^T : (member Foo : string) x)

let inline ll y theta = y*theta-(exp theta)+foo y

您可以通过这样做来减少样板文件:

type IBroadcastable =
| Scalar of D
| Vect of DV

let inline private lift s v = function
| Scalar d -> Scalar (s d)
| Vect dv -> Vect (v dv)

type IBroadcastable with
    static member Exp b = lift exp exp b
    static member Cos b = lift cos cos b
    ...

如果您想支持二进制运算符,可以定义相应的<代码> LIFT2 -但是仔细考虑对于二进制运算符的第一个参数是否是一个“<代码>标量< /COD>值”,第二个是“代码> VECT<代码>(反之亦然)——如果不是,那么你的歧视性结合可能不是一个合适的抽象。

我不明白你为什么需要这种结合。与只在
D
DV
上使用运算符相比,它能给你带来什么?@FyodorSoikin我正在尝试编写一个函数,其中一个参数可以是这两种类型中的任何一种,而且似乎联合是实现这一点的常用方法。注意:如果不清楚的话,我是一个相当新的F#程序员。你会如何使用这个函数?它将如何实施?为什么不能用实现来代替函数呢?我已经意识到,重载exp运算符的正确方法是向联合添加一个静态成员,所以我将更新问题。@FyodorSoikin我添加了一个示例。