2016-11-04 32 views
2

下面的代码犯规编译:哈斯克尔类依赖性

class Foo f where 
    getInt :: f -> Int 

class Bar b where 
    getFooFromBar :: Foo f => b -> f 

someFunction :: Bar b => b -> Int 
someFunction bar = getInt $ getFooFromBar bar 

的错误是Could not deduce (Foo f) arising from a use of 'getInt' from the context (Bar b)

我知道我可以通过改变类Bar修复错误如下:

class Foo f => Bar f b where 
    getFooFromBar :: b -> f 

但我宁愿如果我不必将f添加到Bar的所有实例签名。

有没有办法做到这一点,只保留Foo f约束只有getFooFromBar签名,而不是整个班?

+2

我建议你重新考虑getFooFromBar'的'类型 - 它看起来蓄势的'暧昧类型变量“错误。 – Alec

+1

@Alec完整的错误确实说'类型变量'f0'是不明确的。 – duplode

+0

[理解一个Haskell类型歧义的案例]的可能的重复(http://stackoverflow.com/questions/21220655/understanding-a-case-of-haskell-type-ambiguity) – Cactus

回答

4

但我宁愿如果我没有必要将f添加到Bar的所有实例签名。

从技术上讲,您不需要这样做来编写示例。您可以使用类型注释来指定您在someFunction中使用的Foo的哪个实例,从而解决不明确的类型变量错误。然而,你有更深的问题:

class Foo f where 
    getInt :: f -> Int 

class Bar b where 
    getFooFromBar :: Foo f => b -> f 

也就是说,对于所有的实际目的,是不可能的。 getFooFromBar的类型说您可以使用它来产生的任何类型f的结果,其实例为Foo。但是,如何将这个值实现为f?定义getFooFromBar时无法达到任何特定实例,因为您将从中得到的是Couldn't match type ‘f’ with ‘Blah’类型的错误。对于这个问题的直接解决方案是您建议的问题,原因不同,原因如下:通过Bar实例指定Foo的实例。您可能会发现更好的做一个类型的家庭,而不是多参数类型的类:

{-# LANGUAGE TypeFamilies #-} 
{-# LANGUAGE FlexibleContexts #-} 

class Foo f where 
    getInt :: f -> Int 

class Foo (FooBar b) => Bar b where 
    type FooBar b :: * 
    getFooFromBar :: b -> FooBar b 

instance Foo Char where 
    getInt = const 99 

instance Bar Char where 
    type FooBar Char = Char 
    getFooFromBar = const 'a' 

someFunction :: Bar b => b -> Int 
someFunction = getInt . getFooFromBar 
GHCi> someFunction 'z' 
99