F# return 来自字节数组的指定联合类型

F# return a specified union type from byte array

我正在尝试编写一个接受字节数组并将其转换为参数指定的 ADT 的函数。这在 F# 中可能吗?这是我的 ADT:

type DataFormat =
    | Alphanumeric of string
    | Angle16 of float
    | Angle32 of float
    | Int16 of int
    | Int32 of int

我已经尝试了大约十种不同的方法来格式化函数规范,但我无法弄清楚...我阅读了 SO 上的其他一些帖子,例如 this one 这让我想到了这个比我想象的要复杂。这是我最后两次尝试,但似乎没有任何结果。

// Attempt #1
// This function would require that I pass in a shell
// object as the "format" parameter to make it work, like:
//      let converted = fromBytes1 myArray Angle16(0.0)
let fromBytes1 (b : byte[]) (format : DataFormat) =
    match format with
    | Alphanumeric -> Alphanumeric(BitConverter.ToString(b))
    | Angle16 -> // convert 2-bytes into float...omitted
    | Angle32 -> Angle32(float (BitConverter.ToSingle(b,0)))

// Attempt #2
// the 'T seems to only specify the underlying type like (float, int)
let fromBytes2<'T> (b : byte[]) =
    match 'T with
    | Alphanumeric -> Alphanumeric(BitConverter.ToString(b))
    | Angle16 -> // convert 2-bytes into float...omitted
    | Angle32 -> Angle32(float (BitConverter.ToSingle(b,0)))

我也尝试过使用 typeof<>,但这似乎只是 return 底层基本类型。

我认为你试图在这里双重使用你的受歧视的联盟。

用途 1:您将其用作标记(或 erlang 术语中的原子)以向函数指示您的期望。然后,它看起来像这样:

type DataType = | Int | Bool  // without any data items associated.
let readExpected (valueType : DataType) (data : byte[] ) =
    match valueType with 
    | DataType.Int -> // parse the int from data and do something with it
    | DataType.Bool -> // parse the boolean representation from data ...

用途2:你把union当做真正的ADT。那么它将是您函数的 return 值:

type ADT = | Int of int | Bool of bool // now they have data
let read (data : byte[]) : ADT =
    let wireType = data.[0]
    match wireType with
    | 2uy -> Int(readInt data) // return the parsed int as ADT.Int
    | 3uy -> Bool(readBool data) // return the parsed bool as ADT.Bool
    | _ -> failwith "Unknown wire type in data."

当然,您可以尝试混合使用这两种方法。

type ADT = | Int of int | Bool of bool // now they have data
let readExpected (data : byte[]) (expectedType : ADT) : ADT =
    match expectedType with
    | Int(_) -> Int(readInt data)
    | Bool(_) -> Bool(readBool data)

如果您不喜欢用一些虚假数据内容显式编写 expectedType 的方式,您可以选择像这样的简单解决方案:

type ADT = | Int of int | Bool of bool // now they have data
let IntType = Int(0)
let BoolType = Bool(false)

let readExpected (data : byte[]) (expectedType : ADT) : ADT =
    match expectedType with
    | Int(_) -> Int(readInt data)
    | Bool(_) -> Bool(readBool data)

// caller code:
let intValue = readExpected data IntType
let boolValue = readExpected data BoolType

也许在这里调换两个参数的顺序是有意义的。

如果实际上不是动态的,那么不假装它是动态的也许是有道理的。 如果调用者能够在上面的代码中指定 IntType,您也可以简单地为每种类型创建一个函数。

type ADT = | Int of int | Bool of bool // now they have data
let readInt data =
   Int( foo data) // foo data is my lazy way of saying: However you parse it ;)
let readBool data =
   Bool( if data.[0] = 0uy then false else true )

// caller code:
let intValue = readInt data
let boolValue = readBool data

请注意,所有这些 readXXX 函数都具有相同的类型:byte[] -> ADT。因此,如果您计划通过组合的方式指定消息类型,您可以利用这一事实来发挥自己的优势。