简体   繁体   English

有没有办法将 class 与 F# 中的有区别的联合混合?

[英]Is there a way to mix a class with a discriminated union in F#?

I think it would be convenient to have a discriminated union type with an initializer.我认为使用带有初始化程序的可区分联合类型会很方便。 My idea is to combine something like this:我的想法是结合这样的东西:

type Result =
| Ok of string
| Error of int

With something like that:有这样的事情:

type Result(someData) =
    if SomeData = true then
        this class is Ok "all good"
    else
        this class is Error 100

Is there some clever syntactic way to make an object that can take an input, run some logic and end up with several states that can be used directly in a match statement?是否有一些巧妙的句法方法来制作可以接受输入、运行一些逻辑并最终得到可以直接在 match 语句中使用的多个状态的 object?

(or, if this idea is not good, I'd be happy to hear why too) (或者,如果这个想法不好,我也很乐意听到为什么)

Scott Wlaschin suggests an interesting approach that I think deals with your requests. Scott Wlaschin 提出了一种有趣的方法,我认为它可以处理您的请求。 The basic idea is to define DU constructors as private inside a Module, so that the only way to create a type is by a custom "creation" function inside that module.基本思想是将 DU 构造函数定义为模块内的私有,以便创建类型的唯一方法是通过该模块内的自定义“创建”function。 I inserted Active Patterns in the example bellow to address your "can be used directly in a match statement" request:我在下面的示例中插入了活动模式来解决您的“可以直接在匹配语句中使用”请求:

module User =
    type UserID =
        private //this is important! prevents accessing the constructors outside this module
        | String10Constructor of string
        | String20Constructor of string

    let createUser s =
        if String.length s <= 10 then s |> String10Constructor |> Ok
        elif String.length s <= 20 then s |> String20Constructor |> Ok
        else Error "String lenght should be less than 21."

    let (|User10|User20|) = function
        | String10Constructor s -> User10 s
        | String20Constructor s -> User20 s

open User

let tryToCreateUser s = 
    match createUser s with
    | Ok u ->
        match u with
        | User10 s -> sprintf "User10: %s" s
        | User20 s -> sprintf "User20: %s" s
    | Error e -> e

tryToCreateUser "1111111254545454543345678911" //ouput: "String lenght should be less than 10."

tryToCreateUser "10" //ouput: "User10: 10"

tryToCreateUser "1044646546546466" //ouput: "User20: 1044646546546466"

//you cannot override createUser by trying to access the constructor directly
//(error: "not accessible from this code location)
    let x = String10Constructor ""

The idea isn't feasible for discriminated unions because they are immutable structures, and only class types may take in constructors.这个想法对于有区别的联合是不可行的,因为它们是不可变的结构,并且只有 class 类型可以接受构造函数。

That said, there are alternatives.也就是说,还有其他选择。

Shadowing阴影

type Result =
| Ok of string
| Error of int

let Result someData =
    if someData then
        Ok "all good"
    else
        Error 100

gets you the kind of semantics you'd expect.得到你期望的那种语义。

Factory method工厂方法

type Result =
| Ok of string
| Error of int with
    static member Create(someData) =
        if someData then
            Ok "all good"
        else
            Error 100

and then Result.Create false .然后Result.Create false You can have multiple overloaded factory methods.您可以有多个重载的工厂方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM