views:

141

answers:

2

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?

+9  A: 

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

Brian
nice, thanks =)
ControlFlow
+7  A: 

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)

Tomas Petricek
+1 for a typesafe approach
Dario
That's awesome.
kvb
It's not that great though. You can write `checked { return Int32.MaxValue + 1 }` and it will be actually unchecked, because to make a number _checked_, you need to pass it to `let!` first...
Tomas Petricek