如何避免显式编写复合的KnownNat约束?

时间:2018-02-13 16:55:03

标签: haskell types constraints typeclass

我有一个类型类,它强加class KnownNat (Card a) => HasFin a where type Card a :: Nat ... 约束:

instance HasFin () where
  type Card () = 1
  ...

instance HasFin Bool where
  type Card Bool = 2
  ...

并且,我有几个基本"构建块"类型:

KnownNat

我计划建立许多"复合材料"使用总和和产品来排除这些构建块类型。目前,当我为这些复合类型之一实例HasFin时,我必须明确地编写复合instance (HasFin a, HasFin b, KnownNat (Card a + Card b)) => HasFin (Either a b) where type Card (Either a b) = Card a + Card b ... 约束:

KnownNat (Card a + Card b)

我真的希望必须在上面的代码中编写:(HasFin a, HasFin b) =>

是否有任何类型检查器插件,可以自动从(KnownNat (Card a + Card b)) =>推断到dependency:properties

如果不这样做,我可以写一个蕴涵,提供相同的推断吗?

1 个答案:

答案 0 :(得分:3)

是的,有这样的插件! ghc-typelits-knownnat

使用示例:

-- Install ghc-typelits-knownnat via your favorite build tool like any other package
-- then only this line needs to be added to enable the plugin
{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}

-- Nothing special to be done otherwise, type-level programming as usual.
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}

module Card where

import GHC.TypeLits

class KnownNat (Card a) => HasFin a where
  type Card a :: Nat

instance HasFin () where
  type Card () = 1

instance (HasFin a, HasFin b) => HasFin (Either a b) where
  type Card (Either a b) = Card a + Card b

这是使用constraints库的另一种没有插件的技术。它定义GADT以捕获约束和蕴涵作为价值级词典,并提供一些公理,包括​​(KnownNat a, KnownNat b) :- KnownNat (a + b)蕴涵。

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE TypeApplications #-}

module Card where

import Data.Constraint
import Data.Constraint.Nat
import GHC.TypeLits

class HasFin a where
  type Card a :: Nat
  card :: Dict (KnownNat (Card a))

instance HasFin () where
  type Card () = 1
  card = Dict

instance (HasFin a, HasFin b) => HasFin (Either a b) where
  type Card (Either a b) = Card a + Card b
  card =
    case (card @a, card @b, plusNat @(Card a) @(Card b)) of
      (Dict, Dict, Sub Dict) -> Dict