let mapTuple f (a,b) = (f a, f b)
我正在尝试创建一个函数,将函数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)
但是,它会出现类型不匹配错误。
我该怎么办?我想在F#中完成什么呢?
答案 0 :(得分:5)
深入研究第2点可能很有价值:给定map f a b = (f a, f b)
,为什么Haskell推断出比map :: (t1 -> t) -> t1 -> t1 -> (t, t)
更普遍的类型?原因是,一旦你包括更高级别的类型,通常不可能推断单个&#34;最通用的&#34;给定表达式的类型。实际上,鉴于上面的简单定义,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#中你必须跳过一个额外的环:对于一个&#34; forall&#34;没有干净的语法。类型,所以你必须创建一个额外的名义类型。例如,要在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")
请注意,与Gustavo的建议相反,您可以将map
的第一个参数定义为表达式(而不是强制它成为某个单独类型的成员)。另一方面,显然比理想情况更多的样板......
答案 1 :(得分:4)
此问题与Haskell(通过扩展)支持的rank-n types有关,但在.NET类型系统中不支持。
我发现解决此限制的一种方法是使用单个方法而不是函数传递类型,然后使用静态约束定义内联 map 函数,例如假设我有一些通用函数:toString
和toOption
我希望能够将它们映射到不同类型的元组:
type ToString = ToString with static member inline ($) (ToString, x) = string x
type ToOption = ToOption with static member ($) (ToOption, x) = Some x
let inline mapTuple f (x, y) = (f $ x, f $ y)
let tuple1 = mapTuple ToString (true, 42)
let tuple2 = mapTuple ToOption (true, 42)
// val tuple1 : string * string = ("True", "42")
// val tuple2 : bool option * int option = (Some true, Some 42)
ToString
将返回相同的类型,但以任意类型运行。 ToOption
将返回两个不同类型的泛型。
通过使用二元运算符类型推断为您创建静态约束,我使用$
因为在Haskell中它意味着 apply 所以对于haskellers f $ x
来说,一个很好的细节读取已经将x应用于f 。
答案 2 :(得分:2)
冒着说明显而易见的风险,一个足够好的解决方案可能是让mapTuple
采用两个函数而不是一个函数:
let mapTuple fa fb (a, b) = (fa a, fb b)
如果您的原始f
是通用的,则将其作为fa
和fb
传递,将为您提供两个具有您正在寻找的类型的函数的具体实例。在最糟糕的情况下,当a
和b
属于同一类型时,您只需要传递相同的函数两次。