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

时间:2014-06-14 08:25:08

标签: types f# overloading return-type

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

  • 输入 FirstType<'a>

    方法 DoWork ,接受:

    • FirstType<'a>

    • 类型的第一个参数
    • 第二个参数是类型的函数('a - >'b)

    DoWork 方法返回类型为 FirstType<'b>

  • 输入 SecondType<'a>

    方法 DoWork ,接受:

    • SecondType<'a>

    • 类型的第一个参数
    • 第二个参数是类型的函数('a - >'b)

    DoWork 方法返回类型为 SecondType<'b>

  • 输入 ThirdType <'a>

    方法 DoWork ,接受:

    • ThirdType <'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&lt;'a&gt;的对象。或者到SecondType&lt;'a&gt;或者对于ThirdType&lt;'a&gt;,second - 类型的函数('a - &gt;'b),其中'b可以是来自'a的另一种类型。所以,这个函数应该确定它的第一个输入参数的类型并基于此 - 返回适当的类型。

执行功能的返回类型:

  • 应该是FirstType&lt;'b&gt;类型的对象。如果Do函数的第一个参数是FirstType&lt;'a&gt;
  • 类型
  • 应该是SecondType&lt;'b&gt;类型的对象如果Do函数的第一个参数是SecondType&lt;'a&gt;
  • 类型
  • 应该是ThirdType&lt;'b&gt;类型的对象。如果Do函数的第一个参数是ThirdType&lt;'a&gt;
  • 类型

所需。这些类的示例用法:

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

出现以下错误:

  

错误:此方法的一个或多个重载具有curried参数。考虑重新设计这些成员以采用tupled形式的参数。

尝试将此执行函数用作类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)

..错误列表中出现以下错误:

  

错误:无法根据此程序点之前的类型信息确定方法“Do”的唯一重载。可能需要类型注释。候选人:静态成员Ops.Do:f :('a - &gt;'b)* x:FirstType&lt;'a&gt; - &GT; FirstType&lt;'b&gt;,静态成员Ops.Do:f :('a - &gt;'b)* x:SecondType&lt;'a&gt; - &GT; SecondType&lt;'b&gt;,静态成员Ops.Do:f :('a - &gt;'b)* x:ThirdType&lt;'a&gt; - &GT; ThirdType&LT;'B个

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

有没有办法在重载的成员方法中使用currying?

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

let result = input |> Do someFunction

如果有人有任何想法或建议,我会很高兴。也许我错了。

1 个答案:

答案 0 :(得分:2)

我认为这就是你要做的事情:

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中查找有关内联和重载的详细信息。您似乎正在尝试在包装类上实现通用映射函数,这与Functor Typeclass in Haskell相对应,{{3}}具有函数fmap,与Do函数具有相同的签名,但与论点翻了。