简体   繁体   中英

Can F# force a parameter to be byte[8]?

I need a little utility function that generates a checksum from an array of 8 bytes.

Now, this itself is trivial, except that I can't use byte Checksum(byte[8] input) in C# as a method declaration - I can't specify size of array, so I have to use byte[] which can be any size, and in the method body I then need to check that input is not null and length == 8.

I'm just wondering if F# can do that? I heard that F# has a lot more options to limit acceptable parameters to a function (eg, Discriminated Unions)?

Granted, in this trivial example F# would be overkill, but I'm curious.

Assuming you don't want the caller to pass them in separately, the byte[] arg is IMO fine; the only change I'd make is that I'd consider passing in a start offset too (since I often find myself working away from the start of a byte[] - but it depends on your context).

If you really want to avoid this ugliness, you could consider treating it as a long (or arguably better (because of right-shift of negatives) for bit-work: ulong ). You always know that is exactly 8 bytes in length, and it avoids a few lookups to boot.

You can switch between the two formats either by shifting or by unsafe cast of a byte* and ulong*

I would say the proper way to go about it would be to create a type that wraps a byte[8] and use that as a parameter.


With that said, I can think of using active patterns, but seems like overkill:

let (|Is8|NotIs8|) (input : byte[]) = if input.Length = 8 then Is8 else NotIs8


let testFunction (a : 'T[]) = 
    match a with
    | Is8 -> printfn "yes it is 8 in size"
    | NotIs8 ->  printfn "it is not 8 in size"

Neither of the two languages can specify that a function takes an array of specific length. The problem with this is that checking whether the caller actually provides array with that length is quite difficult. It can be checked when the array is created directly by new byte[8] , but all other cases require some tricky techniques.

This is partly done by Code Contracts , which is a tool for additional checking that can be installed into Visual Studio and used with both C# and F#. It allows you to write something like:

byte CheckSum(byte[] data) {
  Contract.Requires(data != null);
  Contract.Requires(data.Length == 8);
  // Implementation
}

Code Contracts come with a tool that gives you a warning at compile-time when you call CheckSum with a wrongly sized array. It can also generate runtime check (when it cannot statically determine that the call is correct).

In F# (or any other .NET language) a type cannot depend on a value, therefore the answer is no, you can't do that. (Note that you could encode that in a C++ template, as templates can depend on integer parameters.) Using a wrapper type for byte[] merely shifts the runtime length check to the construction of the wrapper object and doesn't really improve the situation.

The only way for actually forcing the correct type at compile time I can think of is using a 8-tuple (or an equivalent type with eight byte fields). That however seems to be an rather ugly solution as it ultimately requires you to spell out explicitly each of the individual bytes. Luckily there already is a primitive type that fulfills the length constraint: ulong as Marc suggested.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM