使用通用量化的返回类型编写函数

Wyz*_*a-- 2 polymorphism haskell types typeclass

如果我写

foo :: (Num a) => a
foo = 42
Run Code Online (Sandbox Code Playgroud)

GHC很乐意接受它,但如果我写的话

bar :: (Num a) => a
bar = (42 :: Int)
Run Code Online (Sandbox Code Playgroud)

它告诉我预期的类型a与推断的类型不匹配Int.我不明白为什么,因为Int是类的一个实例Numa代表.

在尝试编写一个功能时,我遇到了同样的情况,这个功能归结为问题的核心,看起来大致如下:

-- Note, Frob is an instance of class Frobbable
getFrobbable :: (Frobbable a) => Frob -> a
getFrobbable x = x
Run Code Online (Sandbox Code Playgroud)

是否可以编写这样的函数?如何使结果与类型签名兼容?

luq*_*qui 11

您正在将类型类约束视为子类型约束.这是常见的事情,但实际上它们只在逆变情况下重合,即在涉及函数参数而不是结果时.签名:

bar :: (Num a) => a
Run Code Online (Sandbox Code Playgroud)

表示调用者可以选择类型a,前提是它是一个实例Num.所以在这里,来电者可以选择打电话,bar :: Int或者bar :: Double他们都应该工作.所以bar :: (Num a) => a必须能够构造任何类型的数字,bar不知道选择了哪种具体类型.

逆变情况完全相同,它恰好与OO程序员在这种情况下的直觉相对应.例如.

baz :: (Num a) => a -> Bool
Run Code Online (Sandbox Code Playgroud)

意味着调用者a再次选择类型,并且再次baz不知道选择了哪种特定类型.

如果您需要让被调用者选择结果类型,只需更改函数的签名以反映此知识.例如.:

bar :: Int
Run Code Online (Sandbox Code Playgroud)

或者在你的getFrobbable情况下,getFrobbable :: Frob -> Frob(这使得功能变得微不足道).在(Frobbable a)发生约束的任何地方,a Frob都会满足它,所以只需说出来Frob.

这可能看起来很尴尬,但实际上只是信息隐藏发生在函数式编程的不同边界.有一种方法可以隐藏特定的选择,但它并不常见,我认为在大多数使用它的情况下都是错误的.