在我的数据
中my_container.default_page = new_collection.id
我想将data User = User { uRank :: Int, uProgress :: Int }
限制为值列表[-1,1,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} @-}
也就是说,RestrictedInt
是Int
,-1
,1
或3
。请注意,您可以轻松编写范围和其他限制,它不必是一些无意义的合法值枚举。
现在我们使用这种受限制的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)}