F#中的类型与模块 [英] Types vs. Modules in F#

查看:54
本文介绍了F#中的类型与模块的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

中的答案对类型中的静态字典感到困惑,在F#中给出了一个建议:,并且一般来说:尝试使用更少的类以及更多的模块和函数;它们在F#中更惯用,并且导致的问题更少

The answer on Confused about static dictionary in a type, in F# finished with one advice: and just in general: try to use fewer classes and more modules and functions; they're more idiomatic in F# and lead to fewer problems in general

这是一个很好的观点,但是我30年的OO才还不想放弃课程(尽管当我们离开C时,我像疯狂地与C ++作战...)

Which is a great point, but my 30 years of OO just don't want to give up classes just yet (although I was fighting against C++ like crazy when we moved away from C...)

所以让我们来看一个实际的现实世界对象:

so let's take a practical real world object:

type Currency =
    {
        Ticker: string
        Symbol: char
    }

and MarginBracket =
    {
        MinSize:           decimal
        MaxSize:           decimal
        Leverage:          int
        InitialMargin:     decimal
        MaintenanceMargin: decimal
    }

and Instrument =
    {
        Ticker:             string
        QuantityTickSize:   int
        PriceTickSize:      int
        BaseCurrency:       Currency
        QuoteCurrency:      Currency
        MinQuantity:        decimal
        MaxQuantity:        decimal
        MaxPriceMultiplier: decimal
        MinPriceMultiplier: decimal
        MarginBrackets:     MarginBracket array
    }

    // formatting
    static member private formatValueNoSign (precision: int) (value: decimal) =
        let zeros = String.replicate precision "0"
        String.Format($"{{0:#.%s{zeros}}}", value)

    static member private formatValueSign (precision: int) (value: decimal) =
        let zeros = String.replicate precision "0"
        String.Format($"{{0:+#.%s{zeros};-#.%s{zeros}; 0.%s{zeros}}}", value)


    member this.BaseSymbol  = this.BaseCurrency.Symbol
    member this.QuoteSymbol = this.QuoteCurrency.Symbol

    member this.QuantityToString    (quantity)          = $"{this.BaseSymbol}{Instrument.formatValueSign    this.QuantityTickSize quantity}"
    member this.PriceToString       (price)             = $"{this.QuoteSymbol}{Instrument.formatValueNoSign this.PriceTickSize price}"
    member this.SignedPriceToString (price)             = $"{this.QuoteSymbol}{Instrument.formatValueSign   this.PriceTickSize price}"
    member this.RoundQuantity       (quantity: decimal) = Math.Round (quantity, this.QuantityTickSize)
    member this.RoundPrice          (price : decimal)   = Math.Round (price, this.PriceTickSize)

    // price deviation allowed from instrument price
    member this.LowAllowedPriceDeviation (basePrice: decimal)  = this.MinPriceMultiplier * basePrice
    member this.HighAllowedPriceDeviation (basePrice: decimal) = this.MaxPriceMultiplier * basePrice


module Instrument =
    let private  allInstruments   = Dictionary<string, Instrument>()
    let list     ()               = allInstruments.Values
    let register (instrument)     = allInstruments.[instrument.Ticker] <- instrument
    let exists   (ticker: string) = allInstruments.ContainsKey (ticker.ToUpper())
    let find     (ticker: string) = allInstruments.[ticker.ToUpper()]

在此示例中,有一个 Instrument 对象及其数据,一些帮助程序成员和一个模块,当需要按名称查找对象时,该模块充当存储库(在此是交易代码)的情况,因此它们是已知的并经过格式化,而不是随机字符串)

In this example, there is an Instrument object with its data and a few helper members and a module which acts as a repository when it's time to find an object by name (a trading ticker in this case, so they're known and formatted, it's not a random string)

我可以将帮助成员移至该模块,例如:

I could move the helping member to the module, for example:

member this.LowAllowedPriceDeviation (basePrice: decimal)  = this.MinPriceMultiplier * basePrice

可能会变成:

let lowAllowedPriceDeviation basePrice instrument = instrument.MinPriceMultiplier * basePrice

因此,该对象将变得更简单,并且最终可以转变为简单的存储类型,而无需进行任何扩充.

So the object would become simpler and could eventually be turned into a simple storage type without any augmentations.

但是我想知道实际的好处是什么(让我们考虑一下可读性,可维护性等)?

But I am wondering what are the practical benefits (let's just consider readability, maintainability, etc)?

此外,我不知道如何将其重构为不是一个类,因为在模块中没有一个内部"类并通过该类进行所有操作,但这只会改变它./p>

Also, I don't see how this could be re-structured to not be a class, short of having an 'internal' class in the module and doing all operations through that, but that would just be shifting it.

推荐答案

您关于将 LowAllowedPriceDeviation 转换为模块的直觉是正确的:它可以通过 this 成为函数参数移到末尾.这是一种可接受的模式.

Your intuition about turning LowAllowedPriceDeviation to a module is correct: it could become a function with the this parameter moved to the end. That is an accepted pattern.

Instrument 类型的所有其他方法都一样.并且这两个私有静态方法可以成为模块中的私有函数.完全相同的方法.

Same goes for all other methods on the Instrument type. And the two private static methods could be come private functions in the module. The exact same approach.

问题如何将其重构为非类" ,这使我有些困惑,因为这实际上不是类. Instrument 是一条记录,而不是一个类.您为它提供了一些实例和静态方法的事实并没有使它成为类.

The question "how this could be re-structured to not be a class" confuses me a bit, because this is not actually a class. Instrument is a record, not a class. The fact that you gave it some instance and static methods doesn't make it a class.

最后(尽管从技术上讲,这部分是基于观点的),关于什么是实际的好处" -答案是可组合性".函数可以以方法无法实现的方式构成.

And finally (though, technically, this part is opinion-based), regarding "what are the practical benefits" - the answer is "composability". Functions can compose in the way that methods can't.

例如,假设您想要一种打印多种乐器的方法:

For example, say you wanted a way to print multiple instruments:

let printAll toString = List.iter (printfn "%s" << toString)

看看如何使用 toString 函数对其进行参数化?那是因为我想以不同的方式将其用于打印工具.例如,我可以打印它们的价格:

See how it's parametrized with a toString function? That's because I'd like to use it for printing instruments in different ways. For example, I might print their prices:

printAll priceToString (list())

但是,如果 PriceToString 是一种方法,我将不得不引入一个匿名函数:

But if PriceToString is a method, I'd have to introduce an anonymous function:

printAll (fun i -> i.PriceToString) (list())

这看起来比使用函数要复杂得多,但实际上它很快变得非常复杂.然而,一个更大的问题是,由于类型推断对属性不起作用(因为它不能),所以它甚至无法编译.为了使其能够编译,您必须添加类型注释,使其更加难看:

This looks just a little bit more involved than using a function, but in practice it gets very complicated fast. A bigger problem, however, is that this wouldn't even compile because type inference doesn't work on properties (because it can't). In order to get it to compile, you have to add a type annotation, making it even uglier:

printAll (fun (i: Instrument) -> i.PriceToString) (list())

那只是函数可组合性的一个例子,还有许多其他例子.但是我不想在这个主题上写一篇完整的博客文章,它已经比我想要的要长得多.

That's just one example of function composability, there are many others. But I'd rather not write a whole blog post on this subject, it's already much longer than I'd like.

这篇关于F#中的类型与模块的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆