Last active
December 21, 2022 15:44
-
-
Save Savelenko/7d9f10a92b97389b565413bce40ff583 to your computer and use it in GitHub Desktop.
Representing statically bounded values in F#
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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