如何使函数在fsharp中返回真正不同的类型? [英] How to make a function to return really different types in fsharp?

查看:225
本文介绍了如何使函数在fsharp中返回真正不同的类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Assume that there is a third-party library written in FSharp, it contains several generic classes, for example as follows:

  • 类型 FirstType<'a>

具有方法 DoWork ,该方法接受:

has method DoWork, that accepts:

  • 类型为 FirstType<'a>

第二个参数是类型('a->'b)

second param is a function of type ('a -> 'b)

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

类型 SecondType<'a>

具有方法 DoWork ,该方法接受:

has method DoWork, that accepts:

  • 类型为 SecondType<'a>

第二个参数是类型('a->'b)

second param is a function of type ('a -> 'b)

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

类型 ThirdType <'a>

具有方法 DoWork ,该方法接受:

has method DoWork, that accepts:

  • 类型为 ThirdType <'a>,

第二个参数是类型('a->'b)

second param is a function of type ('a -> 'b)

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

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

These classes have no common interfaces or parent classes, type Object is their only common parent.

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

Each of the types below has one method named DoWork. It accepts two parameters:

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

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

Then this DoWork function should return the object which has type of the class it is situated in, but with generic type parameter equal to '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>,第二个-类型('a->'b)的函数,其中'b可以是'a以外的类型.因此,此函数应确定其第一个输入参数的类型并基于此-返回适当的类型.

It is required to implement a function called Do, that accepts two parameters, first - an object that has type either equal to FirstType<'a> or to SecondType<'a> or to ThirdType<'a>, second - a function of type ('a -> 'b) where 'b can be a type another from 'a. So, this function should determine the type of it's first input parameter and based on this - return appropriate type.

执行函数的返回类型:

  • should be an object of type FirstType<'b> if the first param of Do function was of type FirstType<'a>
  • should be an object of type SecondType<'b> if the first param of Do function was of type SecondType<'a>
  • should be an object of type ThirdType<'b> if the first param of Do function was of type 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#中使用它.我现在正在考虑如何使一个函数返回真正不同的类型,而不是歧视联盟,因为在调用该函数时,它期望一个特定的类型.

I have thought of function overloading, but it is not allowed to be used in F#. I am now thinking of how to make a function return really different types, not Discriminated Unions, because at the point of calling the function it expects a specific type.

更新:

我查看了约翰·帕尔默(John Palmer)的评论,并尝试了它.

I have looked at the comment of John Palmer and tried it.

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类的成员时,存在相同的错误:

The same error is when trying to use this Do function as a member of class 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

以元组形式的参数重新设计Ops类型的Do方法并创建如下Do函数时:

When redesigning Do methods in Ops type with arguments in tupled form and creating Do function like this:

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

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

.. there is the following error in the Error List:

错误:无法根据此程序点之前的类型信息来确定方法'Do'的唯一重载.可能需要类型注释.候选者:静态成员Ops.Do:f :('a->'b)* x:FirstType<'a>-> FirstType<'b>,静态成员Ops.Do:f :('a->'b) * x:SecondType<'a>-> SecondType<'b>,静态成员Ops.Do:f :('a->'b)* x:ThirdType<'a>-> ThirdType<'b>

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>

因此,我只能在指示类名(Ops)和元组形式的情况下使用 Do .据我在错误消息的帮助下可以理解.

So, I am able to use Do only with indicating a class name (Ops) and in a tupled form.. as I have understood with the help of error messages.

有什么方法可以使用带有超载成员方法的curring?

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

The usage I am dreaming of is like this:

let result = input |> Do someFunction

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

I would be happy if someone has any ideas or suggestions. Maybe I am wrong at some point.

推荐答案

我认为这是您要尝试做的事情:

I think this is what you are trying to do:

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)

这些帖子中找到有关内联和重载的更多信息.看来您正在尝试在包装类上实现通用映射函数,这对应于 Haskell中的Functor Typeclass ,具有函数fmap,其签名与Do函数相同,但参数已翻转.

Find more about inline and overloading in these posts. It seems you are trying to implement a generic map function over your wrapper classes, this correspond to the Functor Typeclass in Haskell, which has a function fmap, with the same signature as your Do function but with the arguments flipped.

这篇关于如何使函数在fsharp中返回真正不同的类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆