I'm trying to create a simple on-disk cache, but each time I run the application my structurally equal records have a different hash value.
The behavior seems to be correct (deterministic) when I run it in LINQPad or if the record only contains an integer.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.2</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Include="Program.fs" />
</ItemGroup>
</Project>
type Test = { test : string }
[<EntryPoint>]
let main argv =
{ test = "test" }
|> hash
|> printfn "%i"
0
I would expect that running hash
on structurally equal records would always return the same value.
F# underneat uses standard .NET hashing functions. Those have a special randomized seed applied on each time new process (or AppDomain in old .NET framework) is executed. This makes them non consistent across different processes. The reason for that is security: leaving the hash unchanged would be a vulnerability, which could be used for eg. deterministic hash collision attacks.
If you want to have fast consistent hashes, you'll need something like Murmur3 or CityHash. They are very fast at hashing any sequence of bytes and provide fairly good collision avoidance. However they're not supported out of the box in F#/.NET.
If you want to use something that already exists in .NET standard library, you could probably use MD5, but keep in mind that it's speed is much weaker than the two above. It's also considered to be more prone to collisions.