F#模式匹配元组的类型

Bru*_*eis 10 f# types tuples pattern-matching

我有一个curried函数,我希望它支持不同类型的参数,这些参数不在继承关系上:

type MyType1 = A | B of float
type MyType2 = C | D of int
Run Code Online (Sandbox Code Playgroud)

我试图做的是:

let func x y =
    match (x, y) with
    | :? Tuple<MyType1, MyType1> -> "1, 1"
    | _ -> "..."
Run Code Online (Sandbox Code Playgroud)

然而,这是不可能的.F#抱怨:

类型''a*'b'没有任何正确的子类型,不能用作类型测试或运行时强制的来源.

这样做的优雅方式是什么?

编辑:让我试着澄清一下.

我有两个相似但不同的类型.我可以容易地将一种类型转换为另一种类型 我想定义一个二进制操作,它将作用于那些类型的实体,但我想向客户端公开一个操作.

也就是说,而不是提供:

let op11 (x : MyType1) (y : MyType1) = // do something useful
let op12 (x : MyType1) (y : MyType2) =
    // convert y to MyType1
    let y' = // ...
    // dispatch to op11
    op11 x y'
let op21 (x : MyType2) (y : MyType1) = // similar
let op22 (x : MyType2) (y : MyType2) = // similar
Run Code Online (Sandbox Code Playgroud)

我想要的是将单个函数暴露给客户端代码:

let op (x : obj) (y : obj) = // ...
Run Code Online (Sandbox Code Playgroud)

这就像模拟方法重载的行为一样,但是带有curried函数.

Tom*_*cek 15

您的代码不起作用,因为F#将参数类型概括为类型参数.我认为你不能动态测试一个类型是否'a * 'b可以转换为类型MyType1 * MyType2(虽然这对我来说有点混乱).在任何情况下,您都可以编写一个带有两个类型参数的函数,obj并使用两个:?模式分别测试它们:

type MyType1 = A | B of float 
type MyType2 = C | D of int

let func (x:obj) (y:obj) = 
    match (x, y) with 
    | (:? MyType1 as x1), (:? MyType1 as x2) -> 
        printfn "%A %A" x1 x2
    | _ -> 
        printfn "something else" 

func A (B 3.0) // A B 3.0
func A (D 42)  // something else
Run Code Online (Sandbox Code Playgroud)

无论如何,知道你为什么要这样做会很有趣?可能有更好的解决方案......

编辑(2)因此,从各4两元素的组合T1T2,你想,可以采取3的功能是正确的(T1 * T1,T1 * T2T2 * T2)?在这种情况下,你不能写一个完全安全的curried函数,因为第二个参数的类型将"依赖"第一个参数的类型(如果第一个参数有一个类型T2,那么第二个参数也必须是T2(否则)它也可以T1)).

您可以编写一个安全的非curried函数,它接受以下类型的参数:

type MyArg = Comb1 of T1 * T1 | Comb2 of T1 * T2 | Comb3 of T2 * T2
Run Code Online (Sandbox Code Playgroud)

函数的类型是MyArg -> string.如果你想要一个curried函数,你可以定义一个类型,允许你使用T1T2作为第一个和第二个参数.

type MyArg = First of T1 | Second of T2
Run Code Online (Sandbox Code Playgroud)

然后,你的咖喱功能将是MyArg -> MyArg -> string.但请注意,如果不允许一种参数类型的组合(如果我理解正确,T2 * T1则不应该被允许).在这种情况下,您的函数将只需要抛出异常或类似的东西.


Jon*_*rop 6

实现此目的主要有三种不同的方法.

首先是obj按照你的建议通过向上转换来牺牲静态类型:

let func x y =
  match box x, box y with
  | (:? MyType1 as x), (:? MyType1 as y) ->
      ...
Run Code Online (Sandbox Code Playgroud)

这几乎总是一个糟糕的解决方案,因为它导致引入不必要的运行时类型检查:

  | _ -> invalidArg "x" "Run-time type error"
Run Code Online (Sandbox Code Playgroud)

我见过这个工作的唯一地方是专门为用户调用的库代码,它们可以从F#交互式会话中调用,同时有效地发生编译时和运行时类型错误,动态类型可以更简洁.例如,我们的F#for Visualization库允许用户尝试使用此技术可视化任何类型的任何值,以调用不同(已知)类型的自定义可视化例程(阅读更多).

第二种是用一种类型替换两种不同的类型:

type MyType1 = A | B of float | C | D of int   
Run Code Online (Sandbox Code Playgroud)

第三种解决方案是引入一种新类型,它只统一这两种类型:

type MyType = MyType1 of MyType1 | MyType2 of MyType2
Run Code Online (Sandbox Code Playgroud)