在Haskell类型族中输入歧义 [英] Type ambiguity in Haskell type families
问题描述
我试图将以下类放在一起域
及其实例 TrivialDomain
{ - #LANGUAGE TypeFamilies# - }
data Transition = Transition
class Domain d其中
类型设置d
类型引擎d :: * - > *
top :: Engine d(Set d)
- ...
complement :: Set d - >引擎d(Set d)
exclude :: Set d - >设置d - >引擎d(设置d)
- ...
数据TrivialDomain = TrivialDomain
实例域TrivialDomain其中
类型设置TrivialDomain = [Int]
类型引擎TrivialDomain = IO
top = return [0..10]
- ...
补充a =顶部>> ; =(flip exclude)a
exclude ab = return $ filter(not。(`elem` b))a
- ...
但我不断收到以下错误,我无法理解
test3.hs:25:21:
无法将类型'Engine d0'与'IO'匹配
类型变量'd0'不明确
期望类型:IO(Set d0 )
实际类型:Engine d0(Set d0)
在'(> =)'的第一个参数中,即'top'
在表达式:top>> =(flip exclude)a
test3.hs:25:35:
无法将类型'Set d1'与'[Int]'相匹配
类型变量'd1'不明确
Exp受影响类型:设置d0 - > [Int] - > IO [Int]
实际类型:设置d1 - >设置d1 - >引擎d1(Set d1)
在'flip'的第一个参数中,即'exclude'
在'(> =)'的第二个参数中,即'(flip exclude)
我期望引擎d(Set d)
在实例声明中解析为 IO [Int]
,但似乎并非如此。至少GHC不这么认为。我缺少什么?
在您的情况下,关联类型不足以推断方法的类型。
您有类 Domain d
和 Set
以及引擎
与 d
相关联。这意味着只要我们的程序中有一个已知的 d
实例的已知 d
,GHC就可以解析设置d
和引擎d
。但是这不会倒退。 GHC无法解决 d
或 Engine d
,因为完全可能有不同的 Domain
设置
和引擎
类型。由于您的类方法只提及 Set
和引擎
,所以 $ b , Domain d
永远不能从方法使用中推断出来。
您可以根据自己的目标做几件事情。首先,您可以使 d
取决于集合
和引擎
:
class域设置引擎,其中
类型DomainOf设置引擎:: *
- ...
更一般地说, FunctionalDependencies
为您提供更大的灵活性来强化类型间的依赖关系。例如,你可以特别声明每个 Set
只有一个 d
,这足以恢复好类型推理:
class Domain d set engine | d - >设置引擎,设置 - > d其中
top ::引擎集
complement :: set - >引擎设置
exclude :: set - >设置 - >引擎集
数据TrivialDomain = TrivialDomain
实例Domain TrivialDomain [Int] IO其中
top = return [0..10]
complement a = top>> =(flip exclude)a
exclude ab = return $ filter(not。(`elem` b))a
最后,如果您想使用原始类,则必须添加 Proxy d
参数传递给你的方法,以便使实例和相关类型可解析:
import Data.Proxy
数据转换=转换
类域d其中
类型设置d
类型引擎d :: * - > *
top :: Proxy d - > Engine d(Set d)
complement :: Proxy d - >设置d - > Engine d(Set d)
exclude :: Proxy d - >设置d - >设置d - >引擎d(集合d)
数据TrivialDomain = TrivialDomain
实例域TrivialDomain其中
类型设置TrivialDomain = [Int]
类型引擎TrivialDomain = IO
顶部_ =返回[0..10]
补全da =顶部d>> =(翻转(不包括d))a
排除轻触= return $ filter(not。(`elem` b))a
这里, code>代理服务器d 是指定你想要使用哪个实例。然而,这意味着我们必须在每种方法用法上编写 top(Proxy :: Proxy d)
(类似与其他方法),这是相当繁重。使用GHC 8,我们可以省略代理
s并使用 TypeApplications
代替:
{ - #language TypeApplications,TypeFamilies# }
- ...
实例域TrivialDomain其中
类型设置TrivialDomain = [Int]
类型引擎TrivialDomain = IO
top = return [0..10]
complement a = top @TrivialDomain>> =(flip(排除@TrivialDomain))a
exclude ab = return $过滤器(不是。(`elem` b))a
I am trying put together the following class Domain
and its instance TrivialDomain
{-# LANGUAGE TypeFamilies #-}
data Transition = Transition
class Domain d where
type Set d
type Engine d :: * -> *
top :: Engine d (Set d)
-- ...
complement :: Set d -> Engine d (Set d)
exclude :: Set d -> Set d -> Engine d (Set d)
-- ...
data TrivialDomain = TrivialDomain
instance Domain TrivialDomain where
type Set TrivialDomain = [Int]
type Engine TrivialDomain = IO
top = return [0..10]
-- ...
complement a = top >>= (flip exclude) a
exclude a b = return $ filter (not . (`elem` b)) a
-- ...
but I keep getting the following error which I fail to understand
test3.hs:25:21:
Couldn't match type ‘Engine d0’ with ‘IO’
The type variable ‘d0’ is ambiguous
Expected type: IO (Set d0)
Actual type: Engine d0 (Set d0)
In the first argument of ‘(>>=)’, namely ‘top’
In the expression: top >>= (flip exclude) a
test3.hs:25:35:
Couldn't match type ‘Set d1’ with ‘[Int]’
The type variable ‘d1’ is ambiguous
Expected type: Set d0 -> [Int] -> IO [Int]
Actual type: Set d1 -> Set d1 -> Engine d1 (Set d1)
In the first argument of ‘flip’, namely ‘exclude’
In the second argument of ‘(>>=)’, namely ‘(flip exclude) a’
I would expect Engine d (Set d)
to resolve to IO [Int]
in the instance declaration, which does not seem to be the case. At least GHC does not think so. What am I missing?
In your case, associated types aren't enough to infer the types of the methods.
You have class Domain d
, and Set
and Engine
are associated to d
. This means that whenever there is a known d
in our program with a known Domain d
instance, GHC can resolve Set d
and Engine d
. But this doesn't work backwards. GHC can't resolve d
or a Domain
instance from the presence of a Set d
or an Engine d
, since it's entirely possible that there are different Domain
instances with the same Set
and Engine
types.
Since your class methods only mention Set
and Engine
, Domain d
can never be inferred from method use.
You could do a couple of things depending on your goals.
First, you could make d
depend on Set
and Engine
:
class Domain set engine where
type DomainOf set engine :: *
-- ...
More generally, FunctionalDependencies
gives you much more flexibility to enforce dependencies between types. For example, you can specifically declare that there is only one d
for each Set
, which is enough to recover good type inference:
class Domain d set engine | d -> set engine, set -> d where
top :: engine set
complement :: set -> engine set
exclude :: set -> set -> engine set
data TrivialDomain = TrivialDomain
instance Domain TrivialDomain [Int] IO where
top = return [0..10]
complement a = top >>= (flip exclude) a
exclude a b = return $ filter (not . (`elem` b)) a
Finally, if you want to use your original class, you have to add Proxy d
parameters to your methods, in order to make the instance and the associated types resolvable:
import Data.Proxy
data Transition = Transition
class Domain d where
type Set d
type Engine d :: * -> *
top :: Proxy d -> Engine d (Set d)
complement :: Proxy d -> Set d -> Engine d (Set d)
exclude :: Proxy d -> Set d -> Set d -> Engine d (Set d)
data TrivialDomain = TrivialDomain
instance Domain TrivialDomain where
type Set TrivialDomain = [Int]
type Engine TrivialDomain = IO
top _ = return [0..10]
complement d a = top d >>= (flip (exclude d)) a
exclude d a b = return $ filter (not . (`elem` b)) a
Here, the purpose of Proxy d
is to specify exactly which instance you want to use.
However, this means we have to write top (Proxy :: Proxy d)
on each method usage (similarly with other methods), which is rather onerous. With GHC 8 we can omit Proxy
s and use TypeApplications
instead:
{-# language TypeApplications, TypeFamilies #-}
-- ...
instance Domain TrivialDomain where
type Set TrivialDomain = [Int]
type Engine TrivialDomain = IO
top = return [0..10]
complement a = top @TrivialDomain >>= (flip (exclude @TrivialDomain)) a
exclude a b = return $ filter (not . (`elem` b)) a
这篇关于在Haskell类型族中输入歧义的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!