let mapTuple f (a,b) = (f a, f b)
Run Code Online (Sandbox Code Playgroud)
我正在尝试创建一个函数,将函数应用于f元组中的两个项目,并将结果作为元组返回.F#类型推断表示mapTuple返回'b*'b元组.它还假设a并且b属于同一类型.
我希望能够传递两种不同的类型作为参数.您会认为这不起作用,因为它们都必须作为参数传递给f.所以我认为如果它们从相同的基类继承,它可能会起作用.
对于我想要实现的目标,这是一个不太通用的函数.
let mapTuple (f:Map<_,_> -> Map<'a,'b>) (a:Map<int,double>,b:Map<double, int>) = (f a, f b)
Run Code Online (Sandbox Code Playgroud)
但是,它会出现类型不匹配错误.
我该怎么做?我想在F#中实现什么?
古斯塔沃大多是对的; 你要求的是需要更高等级的类型.然而,
挖掘到第2点可能很有价值:给定map f a b = (f a, f b),为什么Haskell不推断出比一般类型更多map :: (t1 -> t) -> t1 -> t1 -> (t, t)?原因是,一旦包含更高级别的类型,通常不可能推断给定表达式的单个"最一般"类型.实际上,map鉴于上面的简单定义,有许多可能的更高级别的签名:
map :: (forall t. t -> t) -> x -> y -> (x, y)map :: (forall t. t -> z) -> x -> y -> (z, z)map :: (forall t. t -> [t]) -> x -> y -> ([x], [y])(加上无限多).但请注意,这些都是彼此不兼容的(没有一个比另一个更通用).鉴于第一个你可以调用map id 1 'c',给定第二个你可以调用map (\_ -> 1) 1 'c',给定第三个你可以调用map (\x -> [x]) 1 'c',但这些参数只对每个类型有效,而不是与其他类型有效.
因此,即使在Haskell中,您也需要指定要使用的特定多态签名 - 如果您来自更动态的语言,这可能会有点意外.在Haskell中,这是相对干净的(语法是我上面使用的).但是,在F#中你必须跳过一个额外的环:对于"forall"类型没有干净的语法,所以你必须创建一个额外的名义类型.例如,要在F#中编码上面的第一个类型,我会写这样的东西:
type Mapping = abstract Apply : 'a -> 'a
let map (m:Mapping) (a, b) = m.Apply a, m.Apply b
let x, y = map { new Mapping with member this.Apply x = x } (1, "test")
Run Code Online (Sandbox Code Playgroud)
请注意,与Gustavo的建议相反,您可以将第一个参数定义map为表达式(而不是强制它成为某个单独类型的成员).另一方面,显然有更多的样板,而不是理想的......