我有以下类型类:
{-# 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
到Interpreted 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
选择此类实例。
答案 0 :(得分:4)
不。想象一下,如果我们在另一个模块中
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
,但是仍然需要指定您要使用的实例d要使用。
答案 1 :(得分:4)
按现状,int
的类型不明确。这是因为i
仅出现在Context i
和Interpreted i a
内的签名中,它们是类型族,因此Isaac van Bakel's answer所见证的不是(必要的)单射型。模棱两可的类型只能通过类型应用程序来解决(而IMO,这是一种非常好的方法)。
但是,您可以防止歧义。传统方式是使用关联的 data 系列而不是类型系列。这些具有类型信息,因此始终是内射的:
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
另一种选择是较新的injective type families extension。
{-# 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
请注意,在这种情况下,您将无法将Identity
用于另一个Interpretation
实例。