CompilationRepresentationFlags.UseNullAsTrueValue can be used to
Permit the use of null as a representation for nullary discriminators in a discriminated union
Option.None is the most prominent example of this.
Why is this useful? How is a null check better than the traditional mechanism for checking union cases (the generated Tag property)?
It leads to perhaps unexpected behavior:
Some(1).ToString() //"Some(1)"
None.ToString() //NullReferenceException
EDIT
I tested Jack's assertion that comparing to null instead of a static readonly field is faster.
[<CompilationRepresentation(CompilationRepresentationFlags.UseNullAsTrueValue)>]
type T<'T> =
| Z
| X of 'T
let t = Z
Using ILSpy, I can see t compiles to null (as expected):
public static Test.T<a> t<a>()
{
return null;
}
The test:
let mutable i = 0
for _ in 1 .. 10000000 do
match t with
| Z -> i <- i + 1
| _ -> ()
The results:
Real: 00:00:00.036, CPU: 00:00:00.046, GC gen0: 0, gen1: 0, gen2: 0
If the CompilationRepresentation attribute is removed, t becomes a static readonly field:
public static Test.T<a> t<a>()
{
return Test.T<a>.Z;
}
public static Test.T<T> Z
{
[CompilationMapping(SourceConstructFlags.UnionCase, 0)]
get
{
return Test.T<T>._unique_Z;
}
}
internal static readonly Test.T<T> _unique_Z = new Test.T<T>._Z();
And the results are the same:
Real: 00:00:00.036, CPU: 00:00:00.031, GC gen0: 0, gen1: 0, gen2: 0
The pattern match is compiled as t == null in the former case and t is Z in the latter.
The F# compiler sometimes uses null as a representation for None because it's more efficient than actually creating an instance of FSharpOption<'T> and checking the Tag property.
Think about it -- if you have a normal F# type (like a record) that's not allowed to be null, then any pointer to an instance of that type (the pointer used internally by the CLR) will never be NULL. At the same time, if T is a type which can represent n states, then T option can represent n+1 states. So, using null as a representation for None simply takes advantage of that one extra state value which is available by the fact that F# types aren't allow to be null.
If you want to try turning this behavior off (for normal F# types), you can apply [<AllowNullLiteral(true)>] to them.
Jack's answer seems good, but to expand a little bit, at the IL level the CLR provides a specific opcode for loading null values (ldnull) and efficient means of testing for them (ldnull followed by beq/bne.un/ceq/cgt.un). When JITted, these should be more efficient than dereferencing a Tag property and branching accordingly. While the per-call savings are probably small, option types are used frequently enough that the cumulative savings may be significant.
Of course, as you note there is a tradeoff: methods inherited from obj may throw null reference exceptions. This is one good reason to use string x/hash x/x=y instead of x.ToString()/x.GetHashCode()/x.Equals(y) when dealing with F# values. Sadly, there is no (possible) equivalent of x.GetType() for values represented by null.
Related
I have some types that enforce numeric value ranges, and I use them in many files in a single project. They look something like this:
[<Struct>]
type NonNegativeMoney =
new(x) =
if x < 0m then invalidArg "x" "Ruh-roh..."
{ Value = x }
val Value : decimal
static member (+) (x: NonNegativeMoney, y: NonNegativeMoney) = NonNegativeMoney(x.Value + y.Value)
I now want to make these types internal to the assembly and leave only my OO type model public. However, when I flip these types to internal, I got the following compiler error:
The member or object constructor 'op_Addition' is not public. Private
members may only be accessed from within the declaring type. Protected
members may only be accessed from an extending type and cannot be
accessed from inner lambda expressions.
The reason for this, has been addressed in the question Why does the F# compiler fail with this infix operator?. The solution proposed in the answer is to use F# signature files to make the types internal. This works for the OP's scenario in that question, where usage of the operator is limited to a type in the same file. However, I can't seem to find a way to make it work so the operator is accessible from all files in my project. If I use a signature file, it works intra-file, but not inter-file.
Is there any way to make this work so the types are internal to the assembly, but visible across the files in my project? I'd like to keep the operators, as I'm using library functions like Seq.sum that require them on the types being summed.
You can define the overload in a module instead of inside the type:
[<Struct>]
type internal NonNegativeMoney =
new(x) =
if x < 0m then invalidArg "x" "Ruh-roh..."
{ Value = x }
val Value : decimal
let internal (+) (x: NonNegativeMoney) (y: NonNegativeMoney) = NonNegativeMoney(x.Value + y.Value)
...but this will override the normal (+) operator, which is probably why you ruled it out. Using a custom operator (like ++) may be a reasonable compromise.
You can make the operator available to the whole project by marking the module [<AutoOpen>].
I've written a class that represents a binary relation on a set, S, with two fields: that set, S, and a second set of pairs of values drawn from S. The class defines a bunch of properties of relations, such as being single-valued (i.e., being a function, as defined in an "isFunction()" predicate). After the class definition I try to define some subset types. One is meant to define a subtype of these relations that are also actually "functions". It's not working, and it's a bit hard to decode the resulting error codes. Note that the Valid() and isFunction() predicates do declare "reads this;". Any ideas on where I should be looking? Is it that Dafny can't tell that the subset type is inhabited? Is there way to convince it that it is?
type func<T> = f: binRelOnS<T> | f.Valid() && f.isFunction()
[Dafny VSCode] trying witness null: result of operation might violate subset type constraint for 'binRelOnS'
Subset types and non-emptiness
A subset type definition of the form
type MySubset = x: BaseType | RHS(x)
introduces MySubset as a type that stands for those values x of type BaseType that satisfy the boolean expression RHS(x). Since every type in Dafny must be nonempty, there is a proof obligation to show that the type you declared has some member. Dafny may find some candidate values and will try to see if any one of them satisfies RHS. If the candidates don't, you get an error message like the one you saw. Sometimes, the error message will tell you which candidate values Dafny tried.
In your case, the only candidate value that Dafny tried is the value null. As James points out, the value null doesn't even get to first base, because the BaseType in your example is a type of non-null references. If you change binRelOnS<T> to binRelOnS?<T>, then null stands a chance of being a possible witness for showing your subset type to be nonempty.
User-supplied witnesses
Since Dafny is not too clever about coming up with candidate witnesses, you may have to supply one yourself. You do this by adding a witness clause at the end of the declaration. For example:
type OddInt = x: int | x % 2 == 1 witness 73
Since 73 satisfies the RHS constraint x % 2 == 1, Dafny accepts this type. In some programs, it can happen that the witness you have in mind is available only in ghost code. You can then write ghost witness instead of witness, which allows the subsequent expression to be ghost. A ghost witness can be used to convince the Dafny verifier that a type is nonempty, but it does not help the Dafny compiler in initializing variables of that type, so you will still need to initialize such variables yourself.
Using a witness clause, you could attempt to supply your own witness using your original definition of the subset type func. However, a witness clause takes an expression, not a statement, which means you are not able to use new. If you don't care about compiling your program and you're willing to trust yourself about the existence of the witness, you can declare a body-less function that promises to return a suitable witness:
type MySubset = x: BaseType | RHS(x) ghost witness MySubsetWitness()
function MySubsetWitness(): BaseType
ensures RHS(MySubsetWitness())
You'll either need ghost witness or function method. The function MySubsetWitness will forever be left without a body, so there's room for you to make a mistake about some value satisfying RHS.
The witness clause was introduced in Dafny version 2.0.0. The 2.0.0 release notes mention this, but apparently don't give much of an explanation. If you want to see more examples of witness, search for that keyword in the Dafny test suite.
Subset types of classes
In your example, if you change the base type to be a possibly-null reference type:
type func<T> = f: binRelOnS?<T> | f.Valid() && f.isFunction()
then your next problem will be that the RHS dereferences f. You can fix this by weakening your subset-type constraint as follows:
type func<T> = f: binRelOnS?<T> | f != null ==> f.Valid() && f.isFunction()
Now comes the part that may be a deal breaker. Subset types cannot depend on mutable state. This is because types are a very static notion (unlike specifications, which often depend on the state). It would be a disaster if a value could satisfy a type one moment and then, after some state change in the program, not satisfy the type. (Indeed, almost all type systems with subset/refinement/dependent types are for functional languages.) So, if your Valid or isFunction predicates have a reads clause, then you cannot define func in the way you have hoped. But, as long as both Valid and isFunction depend only on the values of const fields in the class, then no reads clause is needed and you are all set.
Rustan
I'm learning F# and I am building a quick set of functions which compare two poker hands and determine the winner.
I made this discriminated union to represent categories of poker hands:
type Category =
| HighCard
| OnePair
| TwoPair
| ThreeOfAKind
| Straight
| Flush
| FullHouse
| FourOfAKind
| StraightFlush
I use this code to compare categories to determine if one hand is better than another:
if playerCategory > houseCategory then Win
elif playerCategory < houseCategory then Loss
// ... More code to handle cases within the same category
So, for example, the expression:
let playerCategory = FullHouse
let houseCategory = HighCard
if playerCategory > houseCategory then Win
elif playerCategory < houseCategory then Loss
// ... Other code
Would have the value Win.
However, I don't understand how the < and > operators are able to work here. (Originally I had a function which mapped each case to a numeric value, but I realized it wasn't necessary.) If I rearrange the order of the cases then the logic breaks, so I'm assuming each case is assigned some default value corresponding to its order within the type?
But I would definitely appreciate a bit more insight...
This is described in the specification:
by default, record, union, and struct type definitions called
structural types implicitly include compiler-generated declarations
for structural equality, hashing, and comparison. These implicit
declarations consist of the following for structural equality and
hashing
8.15.4 Behavior of the Generated CompareTo implementations
If T is a union type, invoke Microsoft.FSharp.Core.Operators.compare
first on the index of the union cases for the two values, and then on
each corresponding field pair of x and y for the data carried by the
union case. Return the first non-zero result.
In addition to what Lee said, there's also in the spec
8.5.4 Compiled Form of Union Types for Use from Other CLI Languages
A compiled union type U has:
...
One CLI instance property U.Tag for each case C. This property fetches or computes an integer tag corresponding to the case.
The compiler-generated CompareTo method uses the backing fields of these properties to determine the index as stipulated in 8.15.4. This is evidenced by IlSpy:
int tag = this._tag;
int tag2 = category._tag;
if (tag != tag2)
{
return tag - tag2;
}
if (this.Tag != 0)
{
return 0;
}
I need to use Some/None options in heavy numerical simulations. The following micro benchmark gives me Fast = 485 and Slow = 5890.
I do not like nulls and even if I liked them I cannot use null because The type 'float' does not have 'null' as a proper value.
Ideally there would be a compiler option that would compile Some/None into value/null so there would be no runtime penalty. Is that possible? Or how shall I make Some/None efficient?
let s = System.Diagnostics.Stopwatch()
s.Start()
for h in 0 .. 1000 do
Array.init 100000 (fun i -> (float i + 1.)) |> ignore
printfn "Fast = %d" s.ElapsedMilliseconds
s.Restart()
for h in 0 .. 1000 do
Array.init 100000 (fun i -> Some (float i + 1.)) |> ignore
printfn "Slow = %d" s.ElapsedMilliseconds
None is actually already represented as null. But since option<_> is a reference type (which is necessary for null to be a valid value in the .NET type system), creating Some instances will necessarily require heap allocations. One alternative is to use the .NET System.Nullable<_> type, which is similar to option<_>, except that:
it's a value type, so no heap allocation is needed
it only supports value types as elements, so you can create an option<string>, but not a Nullable<string>. For your use case this seems like an unimportant factor.
it has runtime support so that boxing a nullable without a value results in a null reference, which would be impossible otherwise
Keep in mind that your benchmark does very little work, so the results are probably not typical of what you'd see with your real workload. Try to use a more meaningful benchmark based on your actual scenario if at all possible.
As a side note, you get more meaningful diagnostics (including garbage collection statistics) if you use the #time directive in F# rather than bothering with the Stopwatch.
Why aren't option types like "int option" compatible with nullable types like "Nullable"?
I assume there is some semantic reason for the difference, but I can't figure what that is.
An option in F# is used when a value may or may not exist. An option has an underlying type and may either hold a value of that type or it may not have a value.
http://msdn.microsoft.com/en-us/library/dd233245%28VS.100%29.aspx
That sure sounds like the Nullable structure.
Because of the runtime representation choice for System.Nullable<'T>.
Nullable tries to represent the absent of values by the null pointer, and present values by pointers to those values.
(new System.Nullable<int>() :> obj) = null
|> printfn "%b" // true
(new System.Nullable<int>(1) :> obj).GetType().Name
|> printfn "%s" // Int32
Now consider strings. Unfortunately, strings are nullable. So this is valid:
null : string
But now a null runtime value is ambiguous - it can refer to either the absence of a value or a presence of a null value. For this reason, .NET does not allow constructing a System.Nullable<string>.
Contrast this with:
(Some (null : string) :> obj).GetType().Name
|> printfn "%s" // Option`1
That being said, one can define a bijection:
let optionOfNullable (a : System.Nullable<'T>) =
if a.HasValue then
Some a.Value
else
None
let nullableOfOption = function
| None -> new System.Nullable<_>()
| Some x -> new System.Nullable<_>(x)
If you observe the types, these functions constrain 'T to be a structure and have a zero-argument constructor. So perhaps F# compiler could expose .NET functions receiving/returning Nullable<'T> by substituting it for an Option<'T where 'T : struct and 'T : (new : unit -> 'T)>, and inserting the conversion functions where necessary..
The two have different semantics. Just to name one, Nullable is an idempotent data constructor that only works on value types, whereas option is a normal generic type. So you can't have a
Nullable<Nullable<int>>
but you can have an
option<option<int>>
Generally, though there are some overlapping scenarios, there are also things you can do with one but not the other.
Key difference is that must test the option type to see if it has a value. See this question for a good description of its semantics: How does the option type work in F#
Again, this is from my limited understanding, but the problem probably lies in how each gets rendered in the IL. The "nullable" structure probably gets handled slightly different from the option type.
You will find that the interactions between various .Net languages really boils down to how the IL gets rendered. Mostof the time it works just fine but on occasion, it causes issues. (check out this). Just when you thought it was safe to trust the level of abstraction. :)