【问题标题】:Haskell: finite types of user-defined values?Haskell:用户定义值的有限类型?
【发布时间】:2016-07-21 13:37:55
【问题描述】:

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 之类的东西。

【问题讨论】:

  • 井类型在运行时被擦除(你能举个例子 - 因为你问的不只是某种isElem value set,其中set是在运行时定义的吗?)
  • 这基本上是我遇到的问题。我认为可能有一些内部表示为 ([allowed], Maybe value),但它没有很好的语义。
  • 你能用这个假设的特征展示一些虚构的代码吗?你说的是依赖类型之类的东西吗?
  • @leftaroundabout True,但有时确实需要随机访问。实际上,我会说如果您使用数组而不需要随机访问,那么您做错了。 :)
  • @chi:好吧,如果您只需要在列表上具有相同复杂性的操作,那么使用数组代替以提高性能仍然是有意义的。但你是对的——意识到我需要随机访问通常会促使我从列表切换到数组(尽管 IntMap 有时可能是更好的选择)。我的观点是,在 Haskell 中,索引的使用频率远低于大多数其他语言,甚至比 R 或 Matlab 等语言更少,它们也大力宣传避免使用索引。

标签: haskell types


【解决方案1】:

不幸的是,您的示例代码过于稀疏,无法表明您的真正意思。我猜你可能是在依赖类型之后,如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

【讨论】:

  • 抱歉稀疏;我发现这是一个难以表达的概念。在快速谷歌之后,似乎我确实想要依赖类型。在这种情况下,其值集取决于运行时定义的内容。我会接受你的回答,但因为我相信这是我在 Haskell 中最接近我的目标。感谢您的帮助。
  • @TomJohnson,你可以在 Haskell 中用单例做你想做的事;它会相当丑陋并且可能效率低下。诀窍是提出一组值的类型级别表示,然后使用 GADT 将其降低到术语级别。查看type-level-sets 包中的Data.Type.Set 以获得一个相对简单(但仍然相当棘手!)的示例。
【解决方案2】:

也许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% 清楚。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多