如何将Int字段限制为一系列值?

时间:2017-01-19 13:32:40

标签: haskell

在我的数据

my_container.default_page = new_collection.id

我想将data User = User { uRank :: Int, uProgress :: Int } 限制为值列表[-1,1,3],例如。

我该怎么做?

3 个答案:

答案 0 :(得分:9)

定义小和类型是此特定问题的最佳答案。您还可以将newtype与智能构造函数一起使用来实现此效果。

newtype Rank = UnsafeMkRank { unRank :: Int }

mkRank :: Int -> Maybe Rank
mkRank i 
    | i `elem` [-1, 1, 3] = Just (UnsafeMkRank i)
    | otherwise = Nothing

现在,如果您只使用安全mkRank构造函数,则可以假设您的Rank值具有您想要的Int值。

答案 1 :(得分:8)

对于那么小的东西,你应该定义一个精确的类型:

data Rank = RankNegOne | RankOne | RankThree  -- There are probably better names
data User = User { uRank :: Rank, uProgress :: Int }

Haskell并没有强迫你将所有内容编码为整数的子集;利用它!

对于较大的子集,这会很难处理,你正在寻找一个依赖类型,Haskell不直接支持(直到最近?)。

答案 2 :(得分:5)

Liquid Haskell在Haskell之上添加了细化类型,可以做到这一点。请参阅教程here的正确部分。

首先,您可以正常定义数据类型。

module User where

data User = User { uRank     :: Int
                 , uProgress :: Int
                 }

接下来,我们将您的限制定义为细化类型。 Liquid Haskell用户使用语法{-@ blah @-}注释注释。我们首先使用-1, 1, 3类型定义RestrictedInt的奇怪限制:

{-@ type RestrictedInt = {v : Int | v == -1 || v == 1 || v == 3} @-}

也就是说,RestrictedIntInt-113。请注意,您可以轻松编写范围和其他限制,它不必是一些无意义的合法值枚举。

现在我们使用这种受限制的int类型在我们的优化语言中重新定义您的数据类型:

{-@ data User = User { uRank     :: RestrictedInt
                     , uProgress :: Int }
  @-}

这与您的定义类似,但使用限制类型而不仅仅是Int。我们可以内联限制而不是类型别名,但是你的User数据类型将是非常难以理解的。

您可以定义好的值,liquid工具不会抱怨:

goodValue :: User
goodValue = User 1 12

但错误的值会导致错误:

badValue :: User
badValue = User 10 12

$ liquid so.hs(或者您的编辑器集成,如果您有此设置)会产生:

**** RESULT: UNSAFE ************************************************************


 so.hs:16:12-18: Error: Liquid Type Mismatch

 16 | badValue = User 10 12
                 ^^^^^^^

   Inferred type
     VV : {VV : GHC.Types.Int | VV == ?a}

   not a subtype of Required type
     VV : {VV : GHC.Types.Int | VV == (-1)
                                || (VV == 1
                                    || VV == 3)}

   In Context
     ?a := {?a : GHC.Types.Int | ?a == (10 : int)}