Рекомендации для глобальных переменных в F#

для проекта, над которым я работаю, мне нужна глобальная переменная(технически я этого не делаю, я мог бы построить ее, а затем передать ее каждому вызову функции, и пусть каждый вызов функции знает об этом, но это кажется таким же хакерским, менее читаемым и больше работы.)

глобальные переменные-это поиск таблиц (Эндшпиль, открытие книги и транспозиции/кэш) для игры.

тот факт, что некоторые из кода могут потерять некоторые из его беспомощного поведения на самом деле короче говоря, да, я знаю, что глобальное изменчивое состояние плохо, это действительно стоит в этом случае (10x + улучшение производительности)

Итак, вот вопрос: "создайте синглтон или используйте статическое значение в статическом классе с комбинаторами"

Они фактически идентичны, но мне любопытно, что люди делали раньше по этой проблеме

или, альтернативно, должен ли я передавать вещь всем (или, по крайней мере, ссылку на нее в любом случае), это действительно лучший ответ?

3 ответов


вот соглашение, используемое в Матричной библиотеке F# PowerPack (\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))

вот решение, подобное тому, которое опубликовано @Yin Zhu, но с использованием абстрактных типов для указания интерфейса использования для изменяемого значения, локального определения для его инкапсуляции и объектных литералов для обеспечения реализации (это взято из Expert F#-который является соавтором 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 }

вы также можете сделать это со статическими полями, например:

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

в другом модуле, просто:

open Common
Common.Queue <- xxxx