Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

F# type definition with expression

Tags:

f#

Is it possible to express something like this:

type id = int > 0

I know its not possible to do statically, since this would mean F# has dependent types. In C# I'm used to do this sort of thing with code contracts and get a runtime enforcement. I'm looking for something similiar here.

Thanks

EDIT: Thank you for all the answers which have various pros and cons. At the monent I'm only using a small subset of F#, a subset of the ocaml core that lends itself easily to program proofs. So no classes.

like image 643
fsl Avatar asked Dec 30 '12 10:12

fsl


3 Answers

Contrary to what others said, I would suggest not using classes here, if I understood your problem correctly.
Since the value is immutable, we need applying constraint only once. Any wrapper classes would be an overhead and load GC. Instead, a simple function will do the job:

let inline constrained predicate errormessage value =
    if not (predicate value)
    then invalidArg "value" errormessage
    else value

let positive =
    constrained (fun x -> x > 0) "Value must be positive"

let int1 = positive 5      // OK
let int2 = positive -3     // ArgumentException

You can do the same for other types:

let mustBeLong =
    constrained (fun (x:string) -> x.Length > 3) "String must be long"

let str1 = mustBeLong "foobar"  // OK
let str2 = mustBeLong "baz"     // ArgumentException

Using the same within a struct:

type Point2D =
   struct 
      val X: int
      val Y: int
      new(x: int, y: int) = { X = positive x; Y = positive y }
   end

let point1 = Point2D(5, 3)  // OK
let point2 = Point2D(5, -2) // ArgumentException
like image 75
bytebuster Avatar answered Oct 26 '22 20:10

bytebuster


Define it as a union type:

type Id = Id of int

and shadow the constructor with another function:

let Id n =
  assert(n > 0)
  Id n
like image 4
J D Avatar answered Oct 26 '22 19:10

J D


In F#, you have to resort to classes and check arguments inside constructors. Other types such as discriminated unions, records and structs have implicit constructors which you can't easily alter.

type Id(i: int) =
    do if i <= 0 then 
         invalidArg "i" "the argument has to be a positive integer"
    member x.Value = i

Pattern matching doesn't play nicely with classes. You can remedy the problem using active patterns:

let (|Id|) (id: Id) = id.Value

let id = Id(1)

match id with
| Id 1 -> printfn "matched"
| _ -> printfn "unmatched"
like image 2
pad Avatar answered Oct 26 '22 19:10

pad