在Haskell中建模域数据 [英] Modeling domain data in Haskell

查看:138
本文介绍了在Haskell中建模域数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

 模块模型(用户)其中

类审计a where
creationDate :: a - >整数
lastUpdatedDate :: a - >整数
creationUser :: a - > User
lastUpdatedUser :: a - >用户

类标识了哪里
id :: a - >整数

数据User = User {userId :: Integer
,userEmail :: String
,userCreationDate :: Integer $ b $,userLastUpdatedDate :: Integer
, userCreationUser :: User
,userLastUpdatedUser :: User
}

实例已识别用户其中
id u = userId u

实例审计用户其中
creationDate u = userCreationDate
lastUpdatedDate u = userLastUpdatedDate
creationUser u = userCreationUser
lastUpdatedUser u = userLastUpdatedUser

我的应用程序大概有20种类型,就像上面的类型一样。当我说像上面的类型,我的意思是他们将有一个ID,审计信息和一些类型特定的信息(例如用户的电子邮件)。



我无法打包的事情是,我的每个字段(例如User.userEmail)都会创建一个新函数 fieldName :: Type - >的FieldType 。有20种不同的类型,名称空间看起来好像会变得相当快。另外,我不喜欢必须命名我的用户ID字段 userId 。我宁愿将它命名为 id 。有没有办法解决这个问题?



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

解决方案

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

  data审计=审计{
creationDate :: Integer,
lastUpdatedDate :: Integer,
creationUser :: User,
lastUpdatedUser :: User
}

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

  data User = User {
userAudit :: Audit,
userId :: Integer,
userEmail :: String
}

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

  class Audited a where 
audit :: a - >审计

类识别出哪里
ident :: a - >整数

然而,随着你的设计的发展,对于那些特种类溶解在稀薄空气中的可能性开放。 类对象类型类 - 其中每个方法接受类型为 a 的单个参数的类型类 - 可以简化它们。



解决这个问题的另一种方法是使用参数类型对对象进行分类:

  data Object a = Object {
objId :: Integer,
objAudit :: Audit,
objData :: a
}

检查出来, Object 是 Functor
$ $ p $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ )

根据我的设计预感,我会更倾向于这样做。如果不知道更多关于计划的信息,很难说哪种方式更好。并且看,这些类型特定物质需要溶解掉。 : - )

I'm working on designing a larger-ish web application using Haskell. This is purely for my education and interest.

I'm starting by writing out my domain/value objects. One example is a User. Here's what I've come up with so far

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

My application will have roughly 20 types like the above type. When I say "like the above type", I mean they will have an id, audit information, and some type-specific information (like email in the case of User).

The thing I can't wrap my mind around is the fact that each of my fields (e.g. User.userEmail) creates a new function fieldName :: Type -> FieldType. With 20 different types, the namespace seems like it'll get pretty full pretty fast. Also, I don't like having to name my User ID field userId. I'd rather name it id. Is there any way around this?

Maybe I should mention that I'm coming from the imperative world, so this FP stuff is pretty new (yet pretty exciting) for me.

解决方案

Yeah, namespacing can be kind of a pain in Haskell. I usually end up tightening up my abstractions until there are not so many names. It also allows for more reuse. For yours, I would make a data type rather than a class for the audit information:

data Audit = Audit {
    creationDate :: Integer,
    lastUpdatedDate :: Integer,
    creationUser :: User,
    lastUpdatedUser :: User
}

And then pair that up with the type-specific data:

data User = User { 
    userAudit :: Audit,
    userId :: Integer,
    userEmail :: String
}

You can still use those typeclasses if you want:

class Audited a where
    audit :: a -> Audit

class Identified a where
    ident :: a -> Integer

However as your design develops, be open to the possibility of those typeclasses dissolving into thin air. Object-like typeclasses -- ones where every method takes a single parameter of type a -- have a way of simplifying themselves away.

Another way to approach this might be to classify your objects with a parametric type:

data Object a = Object {
    objId    :: Integer,
    objAudit :: Audit,
    objData  :: a
}

Check it out, Object is a Functor!

instance Functor Object where
    fmap f (Object id audit dta) = Object id audit (f dta)

I would be more inclined to do it this way, based on my design hunch. It is hard to say which way is better without knowing more about your plans. And look, the need for those typeclasses dissolved away. :-)

这篇关于在Haskell中建模域数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆