【问题标题】:using a function inside a discriminated union在有区别的联合中使用函数
【发布时间】:2015-10-06 13:02:18
【问题描述】:

我想对一个接受 DU 并返回另一个的函数做一些单元测试:

type Commands = 
  | Schedule of string
  | Abandon of string

type Events =
  | Scheduled of string
  | Abandonned of string

函数如下:

let exec state = function
    | Schedule (cmd) -> Choice1Of2( Scheduled("yes")) 
    | Abandon(cmd)  -> Choice1Of2( Abandonned ("no")) 

我的测试如下:

let result:Choice<Events,string> = exec "initial state" <| Schedule("myCommand");;

result 具有以下类型 Choice&lt;Events,string&gt;,我很想获得一些快速功能以便像这样使用它们:

assertEvent Scheduled (fun e -> Assert.Equal("should produce GameScheduled Event",gameScheduled, e)) result

但要做到这一点,我将拥有以下自制断言功能:

let assertEvent<'TEvent> f g result =
    match result  with
        | Choice1Of2(e) -> 
            match e with 
            | f(evt) ->  g(evt)
            | _ -> Assert.None("event not recognised",Some(e)) 
        | Choice2Of2(reason) -> Assert.None("reason",Some(reason))

我期待函数 f 允许动态匹配模式,但事实并非如此。相反,我有以下错误:

The pattern disciminator 'f' is not defined

我在某处做错了吗?我的fsharp技能没那么高……

【问题讨论】:

    标签: f#


    【解决方案1】:

    像f 这样的普通函数不能用作模式鉴别器,但您可以将Active Patterns 作为参数传递:

    let assertEvent<'TEvent> (|F|_|) g result =
        match result  with
            | Choice1Of2(e) -> 
                match e with 
                | F(evt) ->  g(evt)
                | _ -> Assert.None("event not recognised",Some(e)) 
            | Choice2Of2(reason) -> Assert.None("reason",Some(reason))
    

    但是,这确实需要您将活动模式作为参数传递,这有点麻烦:

    assertEvent
        (function Scheduled(x) -> Some x | _ -> None)
        (fun e -> Assert.Equal("should produce GameScheduled Event",gameScheduled, e))
        result
    

    不过,这不是我解决问题的方法。我更喜欢编写一个布尔表达式,尝试提取并比较我想要验证的值。

    对于初学者,您可以创建一个小的通用辅助函数来从Choice&lt;'a, 'b&gt; 中提取其中一个选项:

    let toOption1 = function Choice1Of2 x -> Some x | _ -> None
    

    此函数的类型为Choice&lt;'a,'b&gt; -&gt; 'a option。 (我将把它作为一个练习来定义等效的toOption2 函数。)

    现在您可以定义一个布尔表达式,如果数据存在则提取数据,并将其与预期值进行比较:

    result
    |> toOption1
    |> Option.map (function Scheduled x -> x | _ -> "")
    |> Option.exists ((=) expected)
    

    这是一个布尔表达式,因此您可以使用Unquote 将其转换为断言。这类似于this approach that I've previously described。

    【讨论】:

    • 感谢您的回答,我期待重构我的断言以使它们更易于编写和阅读,这样就无法完成这项工作,但如果没有更好的结果,我会接受它作为答案。再次感谢。
    • @CygnusX-1 对不起,我误解了这个问题。请参阅我的更新答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-14
    • 1970-01-01
    • 2018-11-24
    • 1970-01-01
    • 1970-01-01
    • 2011-10-23
    相关资源
    最近更新 更多