如何使函数在fsharp中返回真正不同的类型?

假设有一个用FSharp编写的第三方库,它包含几个泛型类,例如:

>键入FirstType<‘a>

有方法DoWork,接受:

> FirstType<‘a>类型的第一个参数,
>第二个参数是类型的函数(‘a – >’b)

DoWork方法返回类型是FirstType<‘b>
>键入SecondType<‘a>

有方法DoWork,接受:

> SecondType<‘a>类型的第一个参数,
>第二个参数是类型的函数(‘a – >’b)

DoWork方法返回类型是SecondType<‘b>
>键入ThirdType<‘a>

有方法DoWork,接受:

> FirstType<‘a>类型的第一个参数,
>第二个参数是类型的函数(‘a – >’b)

DoWork方法返回类型是ThirdType<‘b>

这些类没有公共接口或父类,类型Object是它们唯一的公共父类.

以下每种类型都有一个名为DoWork的方法.它接受两个参数:

>第一个具有包含DoWork方法的类的类型
>第二个是函数(它接受类型的param等于类的泛型参数并返回任何类型的元素,称之为’b)

然后,此DoWork函数应返回具有其所在类的类型的对象,但泛型类型参数等于’b.

目前.这些类的示例用法:

let first = new FirstType<int>()
...
// here result is of type FirstType<string>
let result = FirstType.DoWork first (fun x -> "hello" + x.toString())

let second = new SecondType<int>()
...
// here result is of type SecondType<bool>
let result = SecondType.DoWork second (fun x -> 2 = 4)

let third = new ThirdType<string>()
...
// here result is of type ThirdType<int>
let result = ThirdType.DoWork third (fun x -> x.Length())

问题:

需要实现一个名为Do的函数,它接受两个参数,第一个 – 类型等于FirstType<‘a>的对象.或者到SecondType<‘a>或者对于ThirdType<‘a>,second – 类型的函数(‘a – >’b),其中’b可以是来自’a的另一种类型.所以,这个函数应该确定它的第一个输入参数的类型并基于此 – 返回适当的类型.

Do函数的返回类型:

>应该是FirstType<‘b>类型的对象如果Do函数的第一个参数类型是FirstType<‘a>
>应该是SecondType<‘b>类型的对象如果Do函数的第一个参数是SecondType<‘a>类型.
>应该是ThirdType<‘b>类型的对象如果Do函数的第一个参数类型为ThirdType<‘a>

期望.这些类的示例用法:

let first = new FirstType<int>()
let second = new SecondType<int>()
let third = new ThirdType<string>()
...
// here result1 should be of type FirstType<string>
let result1 = Do first (fun x -> "hello" + x.toString())

// here result2 should be of type SecondType<bool>
let result2 = Do second (fun x -> 2 = 4)

// here result3 should be of type ThirdType<int>
let result3 = Do third (fun x -> x.Length())

我曾想过函数重载,但不允许在F#中使用它.我现在正在考虑如何使函数返回真正不同的类型,而不是Discriminated Unions,因为在调用函数时它需要特定类型.

更新:

我查看了约翰帕尔默的评论并试了一下.

type Ops =
    static member Do (f: 'a -> 'b) (x:FirstType<'a>) = ...
    static member Do (f: 'a -> 'b) (x:SecondType<'a>) = ...
    static member Do (f: 'a -> 'b) (x:ThirdType<'a>) = ...

在尝试创建函数时:

let Do f x = Ops.Do f x

有以下错误:

Error: One or more of the overloads of this method has curried arguments. Consider redesigning these members to take arguments in tupled form.

尝试将此Do函数用作类Ops的成员时,同样的错误:

let result1 = first |> Ops.Do(fun x -> x + 2)
let result2 = second |> Ops.Do(fun x -> "hello" + x.ToString())
let result3 = third |> Ops.Do(fun x -> x = 1) sq

当使用tupled形式的参数重新设计Ops类型的Do方法并创建Do函数时,如下所示:

let Do f x = Ops.Do (f, x)

..错误列表中有以下错误:

Error: A unique overload for method ‘Do’ could not be determined based on type information prior to this program point. A type annotation may be needed. Candidates: static member Ops.Do: f:(‘a -> ‘b) * x:FirstType<‘a> -> FirstType<‘b>, static member Ops.Do: f:(‘a -> ‘b) * x:SecondType<‘a> -> SecondType<‘b>, static member Ops.Do : f:(‘a -> ‘b) * x:ThirdType<‘a> -> ThirdType<‘b>

所以,我能够使用Do只指示一个类名(Ops)和一个tupled形式..正如我在错误消息的帮助下理解的那样.

有没有办法能够使用重载成员方法的currying?

我梦寐以求的用法是这样的:

let result = input |> Do someFunction

如果有人有任何想法或建议,我会很高兴.也许在某些方面我错了.

最佳答案 我想这就是你要做的事情:

type FirstType<'a>  = FirstType  of 'a
type SecondType<'a> = SecondType of 'a
type ThirdType<'a>  = ThirdType  of 'a   

type Ops = Ops with
    static member ($) (Ops, FirstType  a) = fun f -> FirstType  (f a)
    static member ($) (Ops, SecondType a) = fun f -> SecondType (f a)
    static member ($) (Ops, ThirdType  a) = fun f -> ThirdType  (f a)

let inline Do f x = (Ops $f) x

let first  = FirstType  10
let second = SecondType 12
let third  = ThirdType  "Hello"

let result1 = Do first  (fun x -> "hello" + x.ToString())
let result2 = Do second (fun x -> 2 = 4)
let result3 = Do third  (fun x -> x.Length)

these posts中找到有关内联和重载的更多信息.看起来你试图在你的包装类上实现一个通用的map函数,这对应于Functor Typeclass in Haskell,它有一个函数fmap,与你的Do函数具有相同的签名但是参数被翻转.

点赞