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