首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >如何使用接口在记录上进行映射

如何使用接口在记录上进行映射
EN

Stack Overflow用户
提问于 2017-07-13 18:54:31
回答 1查看 79关注 0票数 2

给定以下类型和值

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
type Item<'a, 'b> = Item of 'a * 'b

type X<'a, 'b> = {
    y: Item<'a, int>
    z: Item<'b, bool>
}

let a = {
    y = Item (false, 2); 
    z = Item (1, true) 
}

我想创建一个通用的映射函数

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
tmap: X<'a, 'b> -> X<'x, 'y>

使用接口和对象表达式。到目前为止,我的方法是

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
type ITransform<'a, 'b, 'x, 'y> = abstract Apply : Item<'a,'b> -> Item<'x,'y>

let inline tmap (f:ITransform<_,_,_,_>) ({y = yi; z = zi}) =
    {
        y = f.Apply yi
        z = f.Apply zi
    }

但是,我在z = f.Apply zi收到一个错误,因为f被推断为ITransform<'a, int, 'b, int>

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
let mkStringify () =
    { 
        new ITransform<_,_,_,_> with 
            member __.Apply(Item(a,b)) = Item (sprintf "%A" a, b)
    }

let mkDublicate () =
    { 
        new ITransform<_,_,_,_> with 
            member __.Apply(Item(a,b)) = Item ((a, a), b)
    }

let x = tmap (mkStringify()) a
let y = tmap (mkDoublicate()) a

这是How to define a fmap on a record structure with F#的后续问题。

我可以通过使用其中一个答案中描述的静态成员函数方法来解决这个问题,但不能使用接口方法

EN

回答 1

Stack Overflow用户

发布于 2017-07-13 19:21:22

您的ITransform定义并不比一个函数更好。你可以直接使用signature Item<'a,'b> -> Item<'x,'y>的函数,它的工作原理是一样的。

使用接口的原因是,您可以在每次调用该方法时使用不同的泛型参数。但这又意味着泛型参数不能固定在接口本身上。它们必须在方法上:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
type ITransform = abstract Apply<'a, 'b, 'x, 'y> : Item<'a,'b> -> Item<'x,'y>

或者您可以完全删除它们,编译器会将它们从签名中删除:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
type ITransform = abstract Apply : Item<'a,'b> -> Item<'x,'y>

现在tmap可以很好地编译了:尽管接口本身不是泛型的,但它的方法Apply是泛型的,因此它可以在每次调用时使用不同的泛型参数。

然而,现在您有了另一个问题:在mkStringify中实现这样的接口并不那么简单。既然Apply是完全泛型的,那么它的实现就不能返回特定的类型,比如string。你不能既有蛋糕又吃蛋糕:接口是对消费者的“承诺”和对实现者的“要求”,所以如果你的消费者希望能够做“任何事”,那么实现者必须满足并实现“一切”。

要解决这个问题,退一步想想你的问题:你到底想要实现什么?你想把什么转换成什么?到目前为止,在我看来,您试图将所有Item中的第一个参数强制转换为string,同时保持第二个参数不变。如果这是目标,那么ITransform的定义是显而易见的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
type ITransform = abstract Apply : Item<'a,'b> -> Item<string,'b>

这反映了这样的想法:传入Item的第一个参数可以是任何参数,它被转换为string,第二个参数可以是任何参数,并且保持不变。

有了这个定义,tmapmkStringify都可以编译。

如果这不是你的目标,那么请描述一下,我们也许能找到另一个解决方案。但请记住上面与蛋糕相关的一句话:如果您希望tmap支持任何类型,那么ITransform的实现者也必须支持任何类型。

更新

从注释中的讨论可以明显看出,真正的问题描述如下:转换函数应该将Item的第一个参数转换为其他参数,并保持第二个参数不变。对于两个Items来说,“其他的东西”是相同的。

这样,实现就变得清晰了:接口本身应该修复输出的“其他内容”部分,方法应该接受任何类型作为输入:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
type ITransform<'target> = abstract Apply : Item<'a, 'b> -> Item<'target, 'b>

有了这个定义,所有三个函数tmapmkStringifymkDuplicate都可以编译。我们找到了一个共同点:对接口消费者有足够的承诺,对接口实现者没有太多要求。

已经说过了,我认为你并不真的需要一个接口,它太夸张了。不能使用函数的原因是,当通过值传递时,函数将失去其泛型,因此不适用于不同类型的参数。但是,可以通过两次传递该函数来解决此问题。它在这两种情况下都会失去泛型,但它会以不同的方式失去它--即每次都会用不同的参数实例化它。是的,将同一个函数传递两次感觉很笨拙,但它的语法仍然比接口少:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
let inline tmap f1 f2 ({y = yi; z = zi}) =
    {
        y = f1 yi
        z = f2 zi
    }

let stringify x =
    let f (Item(a,b)) = Item (sprintf "%A" a, b)
    tmap f f x

stringify a
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/45088899

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文