在 Haskell 中限制数据类型值中的字符串长度

C. *_*ley 3 haskell types

我正在尝试用 Haskell 表示汽车牌照。车牌格式定义如下:

  1. 旧车牌长度为 5 位字母数字

  2. 新牌照由以下部分组成:

    • 注册状态(N、S、E、W 和 C)
    • 汽车在 (1,2..20) 注册的地区
    • 注册年份和月份
    • 三个字母数字的随机序列。

我对解决方案的尝试如下

import Data.Char
data Locality = N | S | E | W | C

data Reg = OldReg {code :: String} | NewReg {loc :: Locality,
            district :: Int,
            month :: Int,
            year :: Int,
            random:: String} 

currentYear =2020

createNewReg::Localitly->Int->Int->Int->String-> Maybe Reg
createNewReg l d m y r 
    | (d < 1) ||(d > 20)  = Nothing --district must be between 1 and 20
    | (m < 1) || (m > 12) = Nothing -- month must be between 1 and 12
    | (y >= 2020) && y<= currentYear = Nothing -- Year must be after new plates introduce and below or equal to current year 
    | length(r) /= 3 = Nothing  -- random sequence must be 3 digits long
    | not $foldl (&&) True $ map Data.Char.isAlpha r = Nothing --random sequence must be alphanumeric
    | otherwise = Just $NewReg l d m y r 
Run Code Online (Sandbox Code Playgroud)

我想知道是否有更“Haskell”的解决方案来解决这个问题?我可以制作一种限制这些字段的数据类型,而不必使用不同的函数来构造一个吗?

喜欢NewData {random :: String (Alpha :3)}将其限制为长度为 3 的字母数字字符串?

我知道这不会接近正确的语法,但我希望你能明白

Mar*_*ann 5

您可以使用各种奇特的功能扩展 Haskell,这些功能可能会让您有所收获,但也有一些容易实现的成果。正如编程中的典型情况一样,这些通常以权衡的形式出现。这也是这里的情况。

程序员经常使用整数来表示数字,即使这些数字更像是标签或 ID 而不是数字。如果您不对数字进行算术运算,则它们是和类型的候选对象。

例如,您可以这样定义月份:

data Month =
  Jan | Feb | Mar | Apr | May | Jun | Jul | Aug | Sep | Oct | Nov | Dec
  deriving (Eq, Show, Ord, Enum, Bounded)
Run Code Online (Sandbox Code Playgroud)

这比使用 an 更类型安全,Int因为您仅限于这十二个值。你也很好地回避了是否01表示一月等问题。

您可以对地区值(此处未显示)执行相同操作。

同样,如果您知道总是有三个值,则列表不是最好的数据类型。元组更好。

Reg那么,数据类型的部分改进将是这样的:

data Reg =
    OldReg { code :: String }
  | NewReg { loc :: Locality,
             district :: Int,
             month :: Month,
             year :: Int,
             random :: (Char, Char, Char) }
  deriving (Eq, Show)
Run Code Online (Sandbox Code Playgroud)

这并不能解决所有问题,因为random仍然可以填充,例如,('1', '2', '2')。想出正确约束年份的类型也更加困难,因此您可能仍然需要智能构造函数。

不过,您可能可以做得更多。通常,车牌只允许大写字母。智能构造函数可以检查小写字母并拒绝它们或转换它们,或者您可以将字母建模为具有 26 个值的和类型:A | B | C | D | ...等等。