F# връща определен тип обединение от байтов масив

Опитвам се да напиша функция, която приема байтов масив и го преобразува в ADT, както е посочено от параметър. Възможно ли е това във F#? Това е моят ADT:

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

Опитах около десет различни начина за форматиране на спецификацията на функцията, но не мога да го разбера... Прочетох някои от другите публикации на SO като този, което ме кара да мисля, че това е по-сложно, отколкото си мисля. Това бяха последните ми два опита, които не доведоха до никъде.

// 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‹>, но изглежда, че връща само базовия тип.


person Eric    schedule 24.04.2015    source източник
comment
можете ли да дадете примерна двойка вход/изход? Наистина нямам представа какво искате да правите с b там (тъй като просто го игнорирате) - освен това можете да използвате match за съвпадение на типове/класове по време на изпълнение с помощта на оператора :?, но не мисля, че това е това, което се опитвате да направите   -  person Random Dev    schedule 24.04.2015
comment
Редактирах кода, надявам се това да помогне. Опитвах се да пропусна действителната работа по преобразуване на байтове, извършвана в различните клонове, за да предотвратя объркване...   -  person Eric    schedule 24.04.2015
comment
Мисля, че знам какво се опитвате да направите - но мисля, че в този случай бих предпочел вместо това да напиша 5-те различни версии на вашата функция за преобразуване   -  person Random Dev    schedule 25.04.2015
comment
друга идея може да е да маркирате байтовете, докато се произвеждат - можете да използвате ADT с един случай или дори фантомни типове за това   -  person Random Dev    schedule 25.04.2015


Отговори (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: Използвате обединението като истински 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)

Ако не харесвате начина за изрично записване на очаквания тип с някакво фалшиво съдържание на данни, можете да изберете лесно решение като това:

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. Така че, ако планирате да посочите типове съобщения чрез композиция, можете да използвате този факт в своя полза.

person BitTickler    schedule 24.04.2015
comment
Разбирам третия ви вариант, защото го бях пробвал, но това изисква нещо като това в кода: let converted = readExpected byteArray Int(0), за да го извикате, нали? Не изглеждаше чисто да се налага да се прави фалшив обект (т.е. Int(0)), който да премине във функцията, за да може да прави съпоставяне на шаблони, но ако това е необходимото, може би това работи добре. - person Eric; 25.04.2015
comment
@Eric прецизира отговор в този аспект - person BitTickler; 25.04.2015
comment
@Eric Наистина има само 2 ситуации, които мога да видя тук: 1. Знаете какво съдържа data[], когато извиквате функцията (или поне очаквате да знаете). 2. Имате информация какво следва в самия масив от данни и вашият обаждащ се няма никакви очаквания. За ситуация 1 можете също така да извикате директно функцията, от която се нуждаете. - person BitTickler; 25.04.2015
comment
Страхотно, благодаря, че му дадохте малко очи. Това поне ми даде проверка на разума, че не пропускам някакъв очевидно лесен израз за съпоставяне на шаблони! - person Eric; 25.04.2015