F# allows to use checked arithmetics by opening Checked
module, which redefines standard operators to be checked operators, for example:
open Checked let x = 1 + System.Int32.MaxValue // overflow
will result arithmetic overflow exception.
But what if I want to use checked arithmetics in some small scope, like C# allows with keyword checked
:
int x = 1 + int.MaxValue; // ok int y = checked { 1 + int.MaxValue }; // overflow
How can I control the scope of operators redefinition by opening Checked
module or make it smaller as possible?
You can always define a separate operator, or use shadowing, or use parens to create an inner scope for temporary shadowing:
let f() = // define a separate operator let (+.) x y = Checked.(+) x y try let x = 1 +. System.Int32.MaxValue printfn "ran ok" with e -> printfn "exception" try let x = 1 + System.Int32.MaxValue printfn "ran ok" with e -> printfn "exception" // shadow (+) let (+) x y = Checked.(+) x y try let x = 1 + System.Int32.MaxValue printfn "ran ok" with e -> printfn "exception" // shadow it back again let (+) x y = Operators.(+) x y try let x = 1 + System.Int32.MaxValue printfn "ran ok" with e -> printfn "exception" // use parens to create a scope ( // shadow inside let (+) x y = Checked.(+) x y try let x = 1 + System.Int32.MaxValue printfn "ran ok" with e -> printfn "exception" ) // shadowing scope expires try let x = 1 + System.Int32.MaxValue printfn "ran ok" with e -> printfn "exception" f() // output: // exception // ran ok // exception // ran ok // exception // ran ok
Finally, see also the --checked+
compiler option:
http://msdn.microsoft.com/en-us/library/dd233171(VS.100).aspx
Here is a complicated (but maybe interesting) alternative. If you're writing something serious then you should probably use one of the Brians suggestions, but just out of curiosity, I was wondering if it was possible to write F# computation expression to do this. You can declare a type that represents int
which should be used only with checked operations:
type CheckedInt = Ch of int with static member (+) (Ch a, Ch b) = Checked.(+) a b static member (*) (Ch a, Ch b) = Checked.(*) a b static member (+) (Ch a, b) = Checked.(+) a b static member (*) (Ch a, b) = Checked.(*) a b
Then you can define a computation expression builder (this isn't really a monad at all, because the types of operations are completely non-standard):
type CheckedBuilder() = member x.Bind(v, f) = f (Ch v) member x.Return(Ch v) = v let checked = new CheckedBuilder()
When you call 'bind' it will automatically wrap the given integer value into an integer that should be used with checked
operations, so the rest of the code will use checked +
and *
operators declared as members. You end up with something like this:
checked { let! a = 10000 let! b = a * 10000 let! c = b * 21 let! d = c + 47483648 // ! return d }
This throws an exception because it overflows on the marked line. If you change the number, it will return an int
value (because the Return
member unwraps the numeric value from the Checked
type). This is a bit crazy technique :-) but I thought it may be interesting!
(Note checked
is a keyword reserved for future use, so you may prefer choosing another name)
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