F#从字节数组返回指定的联合类型

时间:2015-04-24 20:16:01

标签: types f# unions

我试图编写一个接受字节数组的函数,并将其转换为参数指定的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&lt;&gt;但这似乎只返回基础类型。

1 个答案:

答案 0 :(得分:1)

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

使用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。那么它将是你函数的返回值:

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)

如果您不喜欢使用某些虚假数据内容明确地编写expectType的方法,您可以选择这样一个简单的解决方案:

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。因此,如果您计划通过合成来指定消息类型,则可以利用这一事实。