Doesn't it make sense if I do
type Point =
struct
val Row: int
val Column: int
new (row, column) = if row >= 0 && column >= 0 then { Row = row; Column = column }
else failwith "Cooridinators must be non-negative!"
// This is not a valid object construction
static member (+) (x: Point, y: Point) = Point (x.Row + y.Row, x.Column + y.Column)
static member (-) (x: Point, y: Point) = Point (x.Row - y.Row, x.Column - y.Column)
static member (*) (x: Point, a) = Point (x.Row * a, x.Column * a)
static member (*) (a, x: Point) = Point (x.Row * a, x.Column * a)
end
If it was a class then maybe I can raise exception during the do
binding, but in a structure there is no do
, what am I supposed to do?
I figured out that it is possible to add another constructor after failwith
to get around this, but it raises another question, how can I call the implicit constructor? Do I have to construct it explicitly first like
new () = { Row = 0; Column = 0}
// Error structs auto supports a default constructor
If i simply do this using the default constructor
new (row, column) = if row >= 0 && column >= 0 then { Row = row; Column = column }
else
failwith "Cooridinators must be non-negative!"
new Point () // error
It appears to me that Point ()
returns a unit rather than Point
?
I think the F# compiler complains because constructors should always have a structure:
new (
patterns) =
expressions{
initialization} [then
expressions]
So, the part that initializes the fields cannot be nested under if
or inside any other expression. You can throw exceptions either before the initialization or after (if you add then
keyword). (This matters for classes with inheritance, but I don't think it makes any difference for structs.)
So, one way to write the code is to write:
type Point =
struct
val Row: int
val Column: int
new (row, column) =
if row < 0 || column < 0 then failwith "Cooridinators must be non-negative!"
{ Row = row; Column = column }
// (Static members omitted)
end
Note that I had to negate the condition, because you need to specify the case when you want to throw the exception (rather than saying when you can construct the object). The other alternative would be:
new (row, column) =
{ Row = row; Column = column }
then if row < 0 || column < 0 then failwith "Cooridinators must be non-negative!"
You have another alternative to throw exceptions in initialization section:
new (row, column) =
{ Row = ((if row < 0 || column < 0 then failwith "Coordinators must be non-negative!"); row);
Column = column }
Remember that validating struct constructors is not always a good idea. As you found out, you cannot control initialization of default constructors. If it is a class, you can make sure all constructors are validated in the ways you want.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With