我想写一个有构造函数的类。例如:
class A a where
-- `a`'s field
type T a
-- default value
defT :: T a
-- constructor
mk :: T a -> a
我还需要一个默认构造函数:
mkDef :: A a => a
mkDef = mk defT
但是我收到了编译错误:
Couldn't match expected type ‘T a’ with actual type ‘T a1’
NB: ‘T’ is a type function, and may not be injective
The type variable ‘a1’ is ambiguous
Relevant bindings include
mkDef :: a
In the first argument of ‘mk’, namely ‘defT’
In the expression: mk defT
我认为编译器不知道A a
的第一个参数属于mk
的哪个实例。如果我错了,请纠正我。
尝试使用ScopedTypeVariables
在这里没有帮助,我得到的编译错误大致相同:
mkDef :: forall a . A a => a
mkDef = mk (defT :: T a)
另一个想法是使用FunctionalDependencies
:
class A' a t | a -> t where
-- Get t from a
getT' :: a -> t
defT' :: t
mk' :: t -> a
但是,我仍然无法使用默认构造函数:
mkDef' :: forall a t . A' a t => a
mkDef' = make def
where
def :: t
def = defT' `asTypeOf` (getT' (undefined :: a))
make :: t -> a
make = mk'
有关a
t
所属的信息丢失了:
Could not deduce (A' a0 t) arising from a use of ‘defT'’
from the context (A' a t)
bound by the type signature for mkDef' :: A' a t => a
The type variable ‘a0’ is ambiguous
Relevant bindings include
def :: t
make :: t -> a
In the first argument of ‘asTypeOf’, namely ‘defT'’
In the expression: defT' `asTypeOf` (getT' (undefined :: a))
In an equation for ‘def’:
def = defT' `asTypeOf` (getT' (undefined :: a))
此外,我不喜欢这种方法,因为所有这些t
都应该是类参数。
我想用TypeFamilies
来解决这个问题,因为我已经有了一些以这种方式编写的代码。另外,请告诉我是否有这个问题的常见解决方案。
答案 0 :(得分:4)
这里的问题是缺乏注入性,因为它通常发生在类型族中。考虑一下。
data T1 = ...
data T2 = ...
instance A T1 where
type T T1 = Int
...
instance A T2 where
type T T2 = Int
...
mk defT :: T1 -- error
最后一行需要defT :: Int
。但是,两个实例都定义defT :: Int ~ T T1 ~ T T2
,因此GHC无法决定选择哪一个。
更糟糕的是,GHC拒绝类型类中的defT :: T a
类型,因为它含糊不清:即使我们知道我们想要defT :: SomeGivenType
,GHC也无法理解a
它应该选择。
在我看来,最简单的解决方案是:1)允许含糊不清的类型,以及2)明确选择实例。
{-# LANGUAGE AllowAmbiguousTypes, TypeApplications #-}
mk (defT @ T1) -- after the @ we explicitly pass the type "a"
答案 1 :(得分:2)
我怀疑你想要一个新的数据类型,而不是一个类。例如:
data A a = Mk { get :: a }
这为你定义了一堆东西,但特别感兴趣的是这两个函数:
Mk :: a -> A a
get :: A a -> a
您可以拥有多个字段;例如,在:
data Counted a = Counted { value :: a, count :: Int }
现在存在这些功能:
Counted :: a -> Int -> Counted a
value :: Counted a -> a
count :: Counted a -> Int