Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

F# Checked Arithmetics Scope

Tags:

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?

like image 355
controlflow Avatar asked Feb 16 '10 07:02

controlflow


2 Answers

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

like image 77
Brian Avatar answered Oct 03 '22 07:10

Brian


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)

like image 27
Tomas Petricek Avatar answered Oct 03 '22 06:10

Tomas Petricek