我可以选择一个实例而不使用TypeApplications吗?

时间:2018-09-19 20:03:29

标签: haskell type-families

我有以下类型类:

{-# 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)

在某些情况下,基本上将aInterpreted 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选择此类实例。

2 个答案:

答案 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 iInterpreted 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实例。