在一个有区别的联盟中使用一个函数

时间:2015-10-06 13:02:19

标签: f#

我想对接受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");;

结果有以下类型Choice<Events,string>,我本来希望得到一些快速功能,以便像这样使用它们:

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技能不是那么高......

1 个答案:

答案 0 :(得分:4)

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<'a, 'b>中提取其中一个选项:

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

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

现在你可以定义一个布尔表达式来拉出数据,如果它存在,并将它与预期值进行比较:

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

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