检索有界枚举的大小作为 Nat
Retrieving the size of a Bounded Enum as a Nat
对于我正在编写的库,我希望能够检索具有 Bounded
和 Enum
约束的任何类型的大小,作为类型级别 Nat
.目的是定义类型类实例如:
instance ( Enum a, Bounded a, n ~ BoundedEnumSize a ) => Action ( CyclicGroup n ) ( CyclicEnum a ) where
...
是否有可能使用模板 Haskell 来实现此目的,例如
class ( Enum a, Bounded a ) => BoundedEnum a where
type FiniteEnumSize a :: Nat
instance ( Enum a, Bounded a ) => BoundedEnum a where
type BoundedEnumSize a = ... Template Haskell ... 1 + fromEnum maxBound - fromEnum minBound
我能想到的唯一其他 "solution" 是为同时具有 Enum
和 Bounded
实例的所有类型手动定义 BoundedEnum
实例,但这会导致库的用户有许多孤立实例(因为如果不导入整个宇宙,我将无法定义所有必要的实例)。
这是 Generics 的解决方案:
{-# LANGUAGE DeriveGeneric,UndecidableInstances,TypeFamilies,FlexibleInstances #-}
{-# LANGUAGE DataKinds,ConstraintKinds,TypeOperators,TypeApplications #-}
import GHC.Generics
import GHC.TypeLits
import Data.Proxy
class (KnownNat (FiniteEnumSize a)) => BoundedEnum' a where
type FiniteEnumSize a :: Nat
type BoundedEnum a = (Bounded a, Enum a, BoundedEnum' a)
instance BoundedEnum' (V1 a) where
type FiniteEnumSize (V1 a) = 0
instance BoundedEnum' (U1 a) where
type FiniteEnumSize (U1 a) = 1
instance BoundedEnum' c => BoundedEnum' (K1 i c a) where
type FiniteEnumSize (K1 i c a) = FiniteEnumSize c
instance BoundedEnum' (f a) => BoundedEnum' (M1 i t f a) where
type FiniteEnumSize (M1 i t f a) = FiniteEnumSize (f a)
instance ( BoundedEnum' (f a), BoundedEnum' (g a)
, KnownNat (FiniteEnumSize (f a) * FiniteEnumSize (g a)) )
=> BoundedEnum' ((f:*:g) a) where
type FiniteEnumSize ((f:*:g) a) = FiniteEnumSize (f a)
* FiniteEnumSize (g a)
instance ( BoundedEnum' (f a), BoundedEnum' (g a)
, KnownNat (FiniteEnumSize (f a) + FiniteEnumSize (g a)) )
=> BoundedEnum' ((f:+:g) a) where
type FiniteEnumSize ((f:+:g) a) = FiniteEnumSize (f a)
+ FiniteEnumSize (g a)
然后你可以做例如
data Foo = Foo0 | Foo1 | Foo2
deriving (Eq, Enum, Bounded, Show, Generic)
instance BoundedEnum' Foo where
type FiniteEnumSize Foo = FiniteEnumSize (Rep Foo ())
main = print (natVal (Proxy :: Proxy (FiniteEnumSize Foo)))
结果:3
.
这也适用于更复杂的 ADT——但请注意 Enum
和 Bounded
可以 而不是 可以简单地派生出这些类型,所以也许更好完全取消那些 classes 并简单地在您自己的 class.
中放置一个 universe
方法
finitary 库正是我要找的,因为它允许在类型级别访问任何有限类型的基数,这适用于包含字段的类型。
对于我正在编写的库,我希望能够检索具有 Bounded
和 Enum
约束的任何类型的大小,作为类型级别 Nat
.目的是定义类型类实例如:
instance ( Enum a, Bounded a, n ~ BoundedEnumSize a ) => Action ( CyclicGroup n ) ( CyclicEnum a ) where
...
是否有可能使用模板 Haskell 来实现此目的,例如
class ( Enum a, Bounded a ) => BoundedEnum a where
type FiniteEnumSize a :: Nat
instance ( Enum a, Bounded a ) => BoundedEnum a where
type BoundedEnumSize a = ... Template Haskell ... 1 + fromEnum maxBound - fromEnum minBound
我能想到的唯一其他 "solution" 是为同时具有 Enum
和 Bounded
实例的所有类型手动定义 BoundedEnum
实例,但这会导致库的用户有许多孤立实例(因为如果不导入整个宇宙,我将无法定义所有必要的实例)。
这是 Generics 的解决方案:
{-# LANGUAGE DeriveGeneric,UndecidableInstances,TypeFamilies,FlexibleInstances #-}
{-# LANGUAGE DataKinds,ConstraintKinds,TypeOperators,TypeApplications #-}
import GHC.Generics
import GHC.TypeLits
import Data.Proxy
class (KnownNat (FiniteEnumSize a)) => BoundedEnum' a where
type FiniteEnumSize a :: Nat
type BoundedEnum a = (Bounded a, Enum a, BoundedEnum' a)
instance BoundedEnum' (V1 a) where
type FiniteEnumSize (V1 a) = 0
instance BoundedEnum' (U1 a) where
type FiniteEnumSize (U1 a) = 1
instance BoundedEnum' c => BoundedEnum' (K1 i c a) where
type FiniteEnumSize (K1 i c a) = FiniteEnumSize c
instance BoundedEnum' (f a) => BoundedEnum' (M1 i t f a) where
type FiniteEnumSize (M1 i t f a) = FiniteEnumSize (f a)
instance ( BoundedEnum' (f a), BoundedEnum' (g a)
, KnownNat (FiniteEnumSize (f a) * FiniteEnumSize (g a)) )
=> BoundedEnum' ((f:*:g) a) where
type FiniteEnumSize ((f:*:g) a) = FiniteEnumSize (f a)
* FiniteEnumSize (g a)
instance ( BoundedEnum' (f a), BoundedEnum' (g a)
, KnownNat (FiniteEnumSize (f a) + FiniteEnumSize (g a)) )
=> BoundedEnum' ((f:+:g) a) where
type FiniteEnumSize ((f:+:g) a) = FiniteEnumSize (f a)
+ FiniteEnumSize (g a)
然后你可以做例如
data Foo = Foo0 | Foo1 | Foo2
deriving (Eq, Enum, Bounded, Show, Generic)
instance BoundedEnum' Foo where
type FiniteEnumSize Foo = FiniteEnumSize (Rep Foo ())
main = print (natVal (Proxy :: Proxy (FiniteEnumSize Foo)))
结果:3
.
这也适用于更复杂的 ADT——但请注意 Enum
和 Bounded
可以 而不是 可以简单地派生出这些类型,所以也许更好完全取消那些 classes 并简单地在您自己的 class.
universe
方法
finitary 库正是我要找的,因为它允许在类型级别访问任何有限类型的基数,这适用于包含字段的类型。