我经常有这个简单的模式:
newtype Username = Username Text
目的当然是在操作"用户名"时提高安全性,而不是将其与其他字段混合。 但是,一直打包和拆包变得乏味。 我想知道是否有技巧可以将"用户名"视为字符串(例如AsString或OverloadStrings),还是它违背了目的?
我认为你的newtype
方法很好。你必须问问自己,为什么你总是需要打包和拆包。对于这样的类型,通常应该有一个定义它的模块、一堆繁琐的 typeclass 实例和一些操作函数。你只是把它砸出来。
{-# language GeneralizedNewtypeDeriving #-}
module MyNamespace.Username (.....) where
import Data.Hashable
newtype Username = Username Text
deriving (Eq, Ord, Show, Hashable)
unUsername :: Username -> Text
validateForm :: Text -> Maybe Username
我认为它违背了目的。理想情况下,您应该只将其打包在输入站点中(例如,在您的 web API 中,通常隐式使用FromJSON
或FromHTTPApiData
之类的东西)并且仅在使用前解压缩(例如,在查询另一个 Web API 时,通过ToJSON
隐式,或者在查询数据库时,希望再次隐式使用某种机制,允许您将Username
"直接"存储在数据库中 - 例如EntityField
持久)
如果你想要正确的类型安全,你应该使用newtype
.这旨在防止您来回隐式转换以防止意外错误。
如果您只是想要一个不妨碍您的类型别名,则应使用 type 关键字:
type Username = Text
这意味着接受Username
的函数可以采用Text
,而无需调用值构造函数等。事实上,String
被定义为type String = [Char]
因此您可以在任何需要字符串的地方提供[Char]
;它们是等效的。(巧合的是,将链表用于字符串而不是向量的效率低下是Text
和ByteString
存在的原因。
您仍然需要转换才能使其成为String
,但是使用这种方法,除了阅读代码的人的语义提示外,Username
和Text
类型之间没有区别。
如果你想在String
之间转换,我对这些语言扩展不太熟悉,但只要你还使用type
别名而不是newtype
定义,它们看起来就可以了。