Haskell 我可以选择一个实例而不使用TypeApplications吗?
我有以下类型的类:Haskell 我可以选择一个实例而不使用TypeApplications吗?,haskell,type-families,Haskell,Type Families,我有以下类型的类: {-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE InstanceSigs #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE MultiParamTypeClasses #-} class Interpretation i where type Context i :: * -> * class Interpretatio
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
class Interpretation i where
type Context i :: * -> *
class Interpretation i => Interpret i a where
type Interpreted i a :: *
int :: a -> Context i (Interpreted i a)
它基本上将解释函数从a
编码到expressed i a
,并封装在某些上下文中
我写了以下实例:
data X = X
instance Interpretation X where
type Context X = Identity
instance Interpret X Int where
type Interpreted X Int = Bool
int :: Int -> Identity Bool
int x = pure (x == 0)
现在假设我需要使用上面的int
将int
解释为Bool
,例如在以下函数中:
f :: Int -> Bool
f = -- use int somehow
如果我将f
定义如下:
f x = int x
编译器将抱怨:
• Couldn't match type ‘Interpreted i0 Int’ with ‘Bool’
Expected type: Int -> Identity Bool
Actual type: Int -> Context i0 (Interpreted i0 Int)
The type variable ‘i0’ is ambiguous
• In the second argument of ‘(.)’, namely ‘int’
In the expression: runIdentity . int
In an equation for ‘f’: f = runIdentity . int
通过使用TypeApplications
我可以选择正确(且仅可用)的实例:
{-# LANGUAGE TypeApplications #-}
-- ...
f :: Int -> Bool
f = runIdentity . int @X
但是我想知道这样的实例是否可以在不使用
TypeApplications
的情况下被选择。想象一下,如果我们在另一个模块中
instance Interpretation Y where
type Context Y = Identity
instance Interpret Y Int where
type Interpreted Y Int = Bool
int :: Int -> Identity Bool
int x = pure (x /= 0)
并定义f
,正如您所做的那样-行为应该是什么
我假设您也不想使用类似于
Proxy
的东西,这将允许您传入类型变量,而无需使用TypeApplications
,但仍然需要指定您想要使用的实例。目前,int
的类型不明确。这是因为i
仅出现在上下文i
和解释i a
中的签名中,这是类型族,因此(不一定)是内射的,如所示。模棱两可的类型只能通过类型应用程序来解决(在我看来,这是一种非常好的方法)
但是,您可以防止歧义。传统的方法是使用关联的数据族而不是类型族;它们具有烘焙的类型信息,因此始终是内射的:
class Interpretation i where
data Context i a
class Interpretation i => Interpret i a where
type Interpreted i a :: *
int :: a -> Context i (Interpreted i a)
instance Interpretation X where
newtype Context X a = XContext (Identity a)
instance Interpret X Int where
type Interpreted X Int = Bool
int :: Int -> Context X Bool
int x = XContext $ pure (x == 0)
f :: Int -> Bool
f n = case int n of
XContext a -> runIdentity a
另一种选择是最近的
请注意,在这种情况下,您将无法将
标识
用于另一个解释
实例。因此,在这种情况下,该模块中只定义(并可访问)一个实例并不重要?@Damianadales,我们对类有一个开放世界的假设。哦,我不知道。有道理。我不知道这些替代方案(以及注入性问题)。谢谢关于使用TypeFamiliesDependencies
的问题,约束r->i
是否意味着不能定义第二个实例,如答案中所示?是的,注入性注释基本上阻止您编写两个都具有类型上下文=标识的实例,从而避免了两种情况之间的歧义。
{-# LANGUAGE TypeFamilyDependencies #-}
class Interpretation i where
type Context i = (r :: * -> *) | r -> i
class Interpretation i => Interpret i a where
type Interpreted i a :: *
int :: a -> Context i (Interpreted i a)
instance Interpretation X where
type Context X = Identity
instance Interpret X Int where
type Interpreted X Int = Bool
int :: Int -> Identity Bool
int x = pure (x == 0)
f :: Int -> Bool
f = runIdentity . int