Gustavo大部分是正确的;您所要求的需要更高级别的类型。 然而,
- .NET(以及F#)支持(一种编码的)更高级别的类型。
- 即使在Haskell中,它支持一种“美好的”方式来表达这样的类型(一旦启用了正确的扩展),它们也不会被推断为您的示例。
深入探讨第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")
请注意,与Gustavo的建议相反,您可以将
map
的第一个参数定义为表达式(而不是强制它成为某个单独类型的成员)。另一方面,很明显有比理想情况下更多的样板文件...
f
就有一个单一的具体类型可以使用,但是否是一个好主意取决于你的问题。 - scrwtp