Haskell 中的简单优化类型
Simple Refinement Types in Haskell
根据 Scott Wlaschin 的 blog post and Book "Domain Modeling made Functional", and from Alexis King's post,我认为域模型应该编码尽可能多的关于域的信息,就像在类型中实际可行的那样,以便“使非法状态无法表示”并获得强有力的保证允许我编写完整的域逻辑函数。
在基本的企业应用中,我们有很多基本的域类型,如街道名称、公司名称、城市等。为了以一种可以防止以后出现大多数错误的形式来表示它们,我想使用一种可以让我
- 限制最大和最小字符数。
- 指定可能使用的字符子集,
- 添加额外的约束,例如没有前导或尾随空格。
我可以想到两种实现此类类型的方法:作为具有智能构造函数和隐藏数据构造函数的自定义抽象数据类型,或者通过某种类型级机制(我模糊地阅读了有关细化类型的内容?可以通过以下方式表示此类类型一些较新的语言扩展?通过 LiquidHaskell?)。哪种方法是明智的?哪种方法最容易适用于在常规 Text
上运行的所有函数,以及我如何最容易地组合两个或多个相同细化类型的值、映射它们等?
理想情况下,会有一个库可以帮助我创建此类自定义类型。有吗?
根据 Alexis King 的博客,我认为合适的解决方案如下所示。当然,其他解决方案也是可行的。
import Control.Monad (>=>)
newtype StreetName = StreetName {getStreetName :: String}
-- compose all validations and wrap them in new constructor.
asStreetName :: String -> Maybe StreetName
asStreetName = StreetName <$> rightNumberOfChars >=> rightCharSubset >=> noTrailingWhiteSpace
-- This funcs will process the string, and produce another validated string or nothing.
rightNumberOfChars :: String -> Maybe String
rightNumberOfChars s = {- ... -}
rightCharSubset :: String -> Maybe String
rightCharSubset s = {- ... -}
noTrailingWhiteSpace :: String -> Maybe String
noTrailingWhiteSpaces = {- ... -}
main = do
street <- readStreetAsString
case asStreetName street of
Just s -> {- s is now validated -}
Nothing -> {- handle the error -}
使 StreetName
成为一个隐藏的构造函数,就像使用 asStreetName
作为一个智能构造函数一样。请记住,其他函数应在类型中使用 StreetName
而不是 String
,以确保数据经过验证。
根据 Scott Wlaschin 的 blog post and Book "Domain Modeling made Functional", and from Alexis King's post,我认为域模型应该编码尽可能多的关于域的信息,就像在类型中实际可行的那样,以便“使非法状态无法表示”并获得强有力的保证允许我编写完整的域逻辑函数。
在基本的企业应用中,我们有很多基本的域类型,如街道名称、公司名称、城市等。为了以一种可以防止以后出现大多数错误的形式来表示它们,我想使用一种可以让我
- 限制最大和最小字符数。
- 指定可能使用的字符子集,
- 添加额外的约束,例如没有前导或尾随空格。
我可以想到两种实现此类类型的方法:作为具有智能构造函数和隐藏数据构造函数的自定义抽象数据类型,或者通过某种类型级机制(我模糊地阅读了有关细化类型的内容?可以通过以下方式表示此类类型一些较新的语言扩展?通过 LiquidHaskell?)。哪种方法是明智的?哪种方法最容易适用于在常规 Text
上运行的所有函数,以及我如何最容易地组合两个或多个相同细化类型的值、映射它们等?
理想情况下,会有一个库可以帮助我创建此类自定义类型。有吗?
根据 Alexis King 的博客,我认为合适的解决方案如下所示。当然,其他解决方案也是可行的。
import Control.Monad (>=>)
newtype StreetName = StreetName {getStreetName :: String}
-- compose all validations and wrap them in new constructor.
asStreetName :: String -> Maybe StreetName
asStreetName = StreetName <$> rightNumberOfChars >=> rightCharSubset >=> noTrailingWhiteSpace
-- This funcs will process the string, and produce another validated string or nothing.
rightNumberOfChars :: String -> Maybe String
rightNumberOfChars s = {- ... -}
rightCharSubset :: String -> Maybe String
rightCharSubset s = {- ... -}
noTrailingWhiteSpace :: String -> Maybe String
noTrailingWhiteSpaces = {- ... -}
main = do
street <- readStreetAsString
case asStreetName street of
Just s -> {- s is now validated -}
Nothing -> {- handle the error -}
使 StreetName
成为一个隐藏的构造函数,就像使用 asStreetName
作为一个智能构造函数一样。请记住,其他函数应在类型中使用 StreetName
而不是 String
,以确保数据经过验证。