Snark
Snark

Reputation: 1664

Style guidelines for global variables in F#

I need a global variable (I could build it, then pass it to every single function call and let every single function call know about it, but that seems just as hacky, less readable and more work). The global variables are lookup tables (endgame: opening book and transpositions/cache) for a game.

That some of the code may lose its idempotent behavior is actually the point (speedups). I know global mutable state is bad but it's worth it (10x+ performance improvement). So how do I build a singleton or use a static value in a static class with combinators?

They are effectively identical but I am curious what people have done on this sort of problem. Or should I be passing the thing around to everyone (or at least a reference)?

Upvotes: 7

Views: 2998

Answers (3)

Bill Donahue
Bill Donahue

Reputation: 126

You can also do it with static fields, like this:

type Common() = 

    static let mutable queue : CloudQueue = null
    static let mutable storageAccount : CloudStorageAccount = null

    static member Queue 
        with get() = queue
        and set v = queue <- v
    static member StorageAccount 
        with get() = storageAccount
        and set v = storageAccount <- v

In another module, just:

open Common
Common.Queue <- xxxx

Upvotes: 3

Alex
Alex

Reputation: 2040

Here is a solution similar to the one posted by @Yin Zhu's, but using abstract types to specify a usage interface for the mutable value, a local definition to encapsulate it and object literals to provide an implementation (this is taken from Expert F#--which is co-authored by Don Syme):

type IPeekPoke =
    abstract member Peek: unit -> int
    abstract member Poke: int -> unit

let makeCounter initialState =
    let state = ref initialState
    { new IPeekPoke with
        member x.Poke(n) = state := !state + n
        member x.Peek() = !state }

Upvotes: 5

Yin Zhu
Yin Zhu

Reputation: 17119

here is the convention used in F# PowerPack Matrix library (\src\FSharp.PowerPackmath\associations.fs):

// put global variable in a special module
module GlobalAssociations =
    // global variable ht
    let ht = 
        let ht = new System.Collections.Generic.Dictionary<Type,obj>() 
        let optab =
            [ typeof<float>,   (Some(FloatNumerics    :> INumeric<float>) :> obj);
              typeof<int32>,   (Some(Int32Numerics    :> INumeric<int32>) :> obj);
                  ...
              typeof<bignum>,  (Some(BigRationalNumerics   :> INumeric<bignum>) :> obj); ]

        List.iter (fun (ty,ops) -> ht.Add(ty,ops)) optab;
        ht

    // method to update ht
    let Put (ty: System.Type, d : obj)  =
        // lock it before changing
        lock ht (fun () -> 
            if ht.ContainsKey(ty) then invalidArg "ty" ("the type "+ty.Name+" already has a registered numeric association");
            ht.Add(ty, d))

Upvotes: 2

Related Questions