在Haskell中建模域数据

thr*_*ups 17 haskell

我正在使用Haskell设计更大的Web应用程序.这纯粹是为了我的教育和兴趣.

我首先写出我的域/值对象.一个例子是用户.这是我到目前为止所提出的

module Model (User) where

class Audited a where
    creationDate :: a -> Integer
    lastUpdatedDate :: a -> Integer
    creationUser :: a -> User
    lastUpdatedUser :: a -> User

class Identified a where
    id :: a -> Integer

data User = User { userId :: Integer
                 , userEmail :: String
                 , userCreationDate :: Integer
                 , userLastUpdatedDate :: Integer
                 , userCreationUser :: User
                 , userLastUpdatedUser :: User
                 }

instance Identified User where
    id u = userId u 

instance Audited User where
    creationDate u = userCreationDate
    lastUpdatedDate u = userLastUpdatedDate
    creationUser u = userCreationUser
    lastUpdatedUser u = userLastUpdatedUser
Run Code Online (Sandbox Code Playgroud)

我的应用程序将有大约20种类型,如上述类型.当我说"像上面的类型"时,我的意思是他们会有一个id,审核信息和一些特定类型的信息(比如用户的电子邮件).

我无法理解的事实是我的每个字段(例如User.userEmail)都创建了一个新函数fieldName :: Type -> FieldType.有20种不同的类型,命名空间似乎很快就会变得非常完整.此外,我不喜欢必须命名我的用户ID字段userId.我宁愿给它起个名字id.有没有办法解决?

也许我应该提一下,我是来自势在必行的世界,所以这个FP的东西对我来说是相当新的(但非常令人兴奋).

luq*_*qui 14

是的,命名空间可能是Haskell的一种痛苦.我通常最终收紧我的抽象,直到没有那么多名字.它还允许更多的重用.对于你的,我会为审计信息创建数据类型而不是类:

data Audit = Audit {
    creationDate :: Integer,
    lastUpdatedDate :: Integer,
    creationUser :: User,
    lastUpdatedUser :: User
}
Run Code Online (Sandbox Code Playgroud)

然后将其与特定类型的数据配对:

data User = User { 
    userAudit :: Audit,
    userId :: Integer,
    userEmail :: String
}
Run Code Online (Sandbox Code Playgroud)

如果需要,您仍然可以使用这些类型类:

class Audited a where
    audit :: a -> Audit

class Identified a where
    ident :: a -> Integer
Run Code Online (Sandbox Code Playgroud)

然而,随着您的设计的发展,对这些类型化合物溶解在稀薄空气中的可能性持开放态度. 类似对象的类型类 - 每个方法都采用单个类型参数的类型a- 有一种简化自己的方法.

另一种解决方法可能是使用参数类型对对象进行分类:

data Object a = Object {
    objId    :: Integer,
    objAudit :: Audit,
    objData  :: a
}
Run Code Online (Sandbox Code Playgroud)

看看,Object是一个Functor!

instance Functor Object where
    fmap f (Object id audit dta) = Object id audit (f dta)
Run Code Online (Sandbox Code Playgroud)

基于我的设计预感,我更倾向于这样做.如果不了解更多有关您的计划,很难说哪种方式更好.看,这些类型的需求消失了.:-)

  • 根据Robin Green的评论,绝对要研究镜头("数据访问器"或"fclabels` on hackage")来处理记录记录(记录......). (2认同)

Joh*_*n L 8

这是Haskell记录的一个已知问题.已经有一些建议(特别是TDNR)来减轻影响,但尚未出现任何解决方案.

如果您不介意将每个数据对象放在一个单独的模块中,那么您可以使用命名空间来区分这些函数:

import qualified Model.User as U
import qualified Model.Privileges as P

someUserId user = U.id user
somePrivId priv = P.id priv
Run Code Online (Sandbox Code Playgroud)

至于用来id代替userId; id默认情况下,隐藏从Prelude导入的内容是可能的.使用以下作为第一个import语句:

import Prelude hiding (id)
Run Code Online (Sandbox Code Playgroud)

现在通常的id功能不在范围内.如果由于某种原因需要它,您可以使用完全限定的名称访问它,即Prelude.id.

在创建与Prelude冲突的名称之前,请仔细考虑.程序员经常会感到困惑,而且工作起来有点尴尬.你可能最好使用简短的通用名称,例如oId.