Haskell的部分功能是将范围检查委托给类型系统(参见例如Numeric.Natural)。对于在运行时定义一组值的类型,这可以做到吗?我有效地喜欢Enum,它的值在编译时是未知的。
编辑:就示例用法而言:
-- Defines the list of allowed values
init :: [a] -> ?
-- Constructs a new instance
construct :: a -> ? -> Maybe Foo
-- Then just usable like an enum
bar :: Int -> Foo -> Bar
理想情况下,我也可以使用Bounded
之类的内容。
答案 0 :(得分:2)
遗憾的是,您的示例代码太稀疏,无法表明您的真实含义。我猜测你可能在依赖类型之后,如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
答案 1 :(得分:1)
也许Set
适合您的需求。我们有:
initialize :: Ord a => [a] -> Set a
initialize = fromList
construct :: Ord a => a -> Set a -> Maybe a
construct x xs = guard (x `member` xs) >> return x
dynamicMinBound :: Set a -> Maybe a
dynamicMinBound xs = fst <$> minView xs
dynamicMaxBound :: Set a -> Maybe a
dynamicMaxBound xs = fst <$> maxView xs
enumerate :: Set a -> [a]
enumerate = toList
dynamicToEnum :: Int -> Set a -> Maybe a
dynamicToEnum n xs = guard (inRange n (0, size xs-1)) >> return (elemAt n xs)
dynamicFromEnum :: Ord a => a -> Set a -> Maybe Int
dynamicFromEnum = lookupIndex
我相信这涵盖了您所要求的操作,但我很容易误解某些内容 - 您的规范并非100%明确。