不幸的是,您的示例代码过于稀疏,无法表明您的真正意思。我猜你可能是在依赖类型之后,如n.m. suggested。如果是这种情况,您可能最好查看 Agda 而不是 Haskell 之类的东西。如果你想要一个更安全的 Daniel Wagner suggested 版本,你可以通过 reflection 包获得它。
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE StandaloneDeriving #-}
module DynEnum (.... not including newtype constructors) where
import Data.Reflection
import Data.Proxy
import Data.Set (Set, splitMember, size, lookupIndex, fromList, elemAt, member, findMin, findMax)
import Data.Foldable
import Data.Bool
import Data.Type.Coercion
-- Just Enum
newtype Limited a s = Limited { unLimited :: a }
type role Limited representational nominal
-- We can safely conflate types of values that come
-- from the same set.
coerceLimited :: (Reifies s (Set a), Reifies t (Set a), Ord a)
=> Maybe (Coercion (Limited a s) (Limited a t))
coerceLimited
| reflect (Proxy :: Proxy s) == reflect (Proxy :: Proxy t)
= Just Coercion
| otherwise = Nothing
instance (Ord a, Reifies s (Set a)) => Enum (Limited a s) where
toEnum i
| 0 <= i && i < size values = Limited $ elemAt i values
| otherwise = error "Limited toEnum: out of range"
where values = reflect (Proxy :: Proxy s)
fromEnum x = case lookupIndex (unLimited x) (reflect x) of
Nothing -> error "Limited fromEnum: out of range"
Just i -> i
enumFrom (Limited a) = case splitMember a (reflect (Proxy :: Proxy s)) of
(_, False, s) -> fmap Limited $ toList s
(_, True, s) -> Limited a : fmap Limited (toList s)
enumFromTo (Limited a) (Limited b) = case splitMember a (reflect (Proxy :: Proxy s)) of
(_, inclFirst, s) -> case splitMember b s of
(t, inclLast, _) -> bool id (Limited a:) inclFirst
. (map Limited (toList t) ++)
$ bool [] [Limited b] inclLast
initialize :: Ord a
=> [a]
-> (forall s . Enum (Limited a s) => Proxy s -> r)
-> r
initialize vals f = reify (fromList vals) f
construct :: forall s a . (Ord a, Reifies s (Set a)) => a -> Maybe (Limited a s)
construct x
| x `member` reflect (Proxy :: Proxy s) = Just (Limited x)
| otherwise = Nothing
newtype Bound a b = Bound a deriving (Enum)
type role Bound representational nominal
instance Reifies b (a, a) => Bounded (Bound a b) where
minBound = Bound . fst $ reflect (Proxy :: Proxy b)
maxBound = Bound . snd $ reflect (Proxy :: Proxy b)
initializeBounded :: (a, a)
-> (forall b . Bounded (Bound a b) => Proxy b -> r)
-> r
initializeBounded bounds f = reify bounds f
newtype LimitedB a s b = LimitedB (Bound (Limited a s) b)
deriving instance (Ord a, Reifies s (Set a)) => Enum (LimitedB a s b)
deriving instance Reifies b (Limited a s, Limited a s) => Bounded (LimitedB a s b)
initializeLimitedB :: Ord a
=> [a]
-> (forall s b . (Enum (LimitedB a s b), Bounded (LimitedB a s b)) => Proxy s -> Proxy b -> r)
-> r
initializeLimitedB [] _f = error "Cannot initialize LimitedB with an empty list"
initializeLimitedB vals f = reify set $ \ps ->
reify (Limited (findMin set), Limited (findMax set)) $ \pb ->
f ps pb
where
set = fromList vals