Skip to content

Instantly share code, notes, and snippets.

@Savelenko
Last active December 21, 2022 15:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Savelenko/7d9f10a92b97389b565413bce40ff583 to your computer and use it in GitHub Desktop.
Save Savelenko/7d9f10a92b97389b565413bce40ff583 to your computer and use it in GitHub Desktop.
Representing statically bounded values in F#
module Bounded =
type IBound =
static abstract Bound : int
type Bounded = private Bounded of int // Abstract data type
let bounded<'bound when 'bound :> IBound> (value : int) : Option<Bounded> =
if value <= 'bound.Bound then Some (Bounded value) else None
let (|Bounded|) (Bounded boundedValue) = boundedValue
module Example =
open Bounded
// If F# had numeric type literals _and_ traits, there would be no need in this type...
type Ten = Ten of Ten with
interface IBound with
static member Bound = 10
let nine = bounded<Ten> 9 // F# BUG? Type parameter can be left out, but a run-time error occurs.
let eleven = bounded<Ten> 11
module BoundedGeneralized =
(* Something funny is going on here (or I am misunderstanding something): syntax 'bound when IBound<'a,'bound> does
not work and the more "explicit" form with :> is required. *)
type IBound<'a,'bound when 'bound :> IBound<'a,'bound>> =
static abstract Bound : 'a
type Bounded<'a,'bound when 'bound :> IBound<'a,'bound>> = private Bounded of 'a // Abstract data type
let bounded<'a,'bound when 'bound :> IBound<'a,'bound> and 'a : comparison> (value : 'a) : Option<Bounded<'a,'bound>> =
if value <= 'bound.Bound then Some (Bounded value) else None
let (|Bounded|) (Bounded boundedValue) = boundedValue
module ExampleGeneralized =
open BoundedGeneralized
type Ten = Ten of Ten with
interface IBound<int,Ten> with
static member Bound = 10
interface IBound<decimal,Ten> with
static member Bound = 10m
let nine = bounded<int,Ten> 9 // Leaving type parameters out here does produce a compilation error, unlike above
let eleven = bounded<decimal,Ten> 11m
[<EntryPoint>]
let main args =
printfn $"9 bounded by 10: %A{Example.nine}" // Some (Bounded 9)
printfn $"11 bounded by 10: %A{Example.eleven}" // None
printfn $"(generalized) 9 bounded by 10: %A{ExampleGeneralized.nine}" // Some (Bounded 9)
printfn $"(generalized) 11m bounded by 10m: %A{ExampleGeneralized.eleven}" // None
0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment