据我所知,F#对存在类型没有任何支持.所以,我正在寻找另一种表达我的想法的方式.
我有一个数据结构,其内容可以用许多不同的方式解释.在这个特定的例子中,我认为它可以被视为一个int或一个真实的:
type Packed = (* something sensible *) unit
type PackedType = | PackedInt
| PackedReal
let undefined<'a> : 'a = failwith "undefined"
let unpackInt : Packed -> int = undefined
let unpackReal : Packed -> real = undefined
let packInt : int -> Packed = undefined
let packReal : real -> Packed = undefined
Run Code Online (Sandbox Code Playgroud)
哪里real有意义,说:
type real = int * int
let addReal : real -> real -> real = undefined
Run Code Online (Sandbox Code Playgroud)
现在,我需要一个功能addPacked : PackedType -> Packed -> Packed -> Packed.我希望它是通用的,即:
type NumberOp = [forall t] { opPack : 't -> Packed; opUnpack : Packed -> 't; opAdd : 't -> 't -> 't }
let getNumberOp (t : PackedType) =
match t with
| PackedInt -> { opPack = packInt; opUnpack = unpackInt; opAdd = (+) }
| PackedReal -> { opPack = packReal; opUnpack = unpackReal; opAdd = addReal }
let addPacked (t : PackedType) (a : Packed) (b : Packed) =
let { opPack = pack; opUnpack = unpack; opAdd = add } = getNumberOp t
pack <| add (unpack a) (unpack b)
Run Code Online (Sandbox Code Playgroud)
在这里,我最终成为了NumberOp一个充满活力的人.所以我在问是否有另一种表达方式.我无法改变Packed,[un]pack*功能和addPacked类型.
我找到了这个答案.它表明存在"众所周知的模式",但文本难以阅读,我无法使其发挥作用.
kvb*_*kvb 13
通常,您可以对类型进行编码
?t.F<t>
Run Code Online (Sandbox Code Playgroud)
如
?x.(?t.F<t> ? x) ? x
Run Code Online (Sandbox Code Playgroud)
遗憾的是,每个通用量化都需要在F#中创建一个新类型,因此忠实编码除了F之外还需要两种类型.以下是我们如何为您的示例执行此操作:
type 't NumberOps = {
opPack : 't -> Packed
opUnpack : Packed -> 't
opAdd : 't -> 't -> 't
}
type ApplyNumberOps<'x> =
abstract Apply : 't NumberOps -> 'x
// ? 't. 't NumberOps
type ExNumberOps =
abstract Apply : ApplyNumberOps<'x> -> 'x
// take any 't NumberOps to an ExNumberOps
// in some sense this is the only "proper" way to create an instance of ExNumberOps
let wrap n = { new ExNumberOps with member __.Apply(f) = f.Apply(n) }
let getNumberOps (t : PackedType) =
match t with
| PackedInt -> wrap { opPack = packInt; opUnpack = unpackInt; opAdd = (+) }
| PackedReal -> wrap { opPack = packReal; opUnpack = unpackReal; opAdd = addReal }
let addPacked (t : PackedType) (a : Packed) (b : Packed) =
(getNumberOps t).Apply
{ new ApplyNumberOps<_> with
member __.Apply({ opPack = pack; opUnpack = unpack; opAdd = add }) =
pack <| add (unpack a) (unpack b) }
Run Code Online (Sandbox Code Playgroud)
不幸的是,这里有很多样板.此外,我已经使用了Apply每个助手类型的抽象成员的无用名称- 如果您能找到您喜欢的名称,请随意替换更有意义的名称.我试图保持相当接近你的风格,但在我自己的代码中,我可能会避免在内部解构记录addPacked,而是直接使用字段访问器.