Background: I am working on my first complete program in Haskell, a simple to do list application on the command line.
背景:我正在开发Haskell中的第一个完整的程序,这是一个简单的清单应用程序。
My question is one of structure and has two levels: (1) What is the best way to do it? and (2) What is the Haskell (functional) way to do it? The reason I phrase it this way is because I suspect that there may be a faster or easier way to do it that ignores the principles of functional programming. I'd like to do it in the more elegant and clear way, as it is more of a learning exercise than anything else.
我的问题是一种结构,有两个层次:(1)最好的方法是什么?(2) Haskell(函数)是怎么做的?我用这种方式来描述它的原因是,我怀疑可能有一种更快或更简单的方法可以忽略函数式编程的原则。我想用更优雅、更清晰的方式来做,因为这更像是一种学习练习。
Keep in mind that I'd (obviously) like the application to be somewhat persistent. The two options on the table right now are to store information in a flat text file or alternately in a Sqlite database.
请记住,我(显然)希望应用程序具有一定的持久性。现在表上的两个选项是将信息存储在平面文本文件中或在Sqlite数据库中交替存储。
The first structure that came to mind was something like this, where a ToDoList type object is simply a list of ToDo items:
我想到的第一个结构是这样的,ToDoList类型的对象仅仅是一个ToDo项的列表:
import Data.List
import Data.Time
data ToDo = ToDo {
todoId :: Int,
todoDue :: ZonedTime,
todoCreated :: UTCTime,
todoItem :: String,
todoPriority :: Priority,
todoStatus :: Status
}
deriving (Show, Read)
type ToDoList = [ToDo]
data Priority = Low | Medium | High
deriving (Show, Read, Eq, Ord)
data Status = Complete | InProgress | Open
deriving (Show, Read, Eq, Ord)
But then I began to wonder how best to store objects of this type. Would this imply that I store them in a flat file? Is there some way to relate objects of highly specified types like this to fields/columns in a database?
但后来我开始思考如何最好地存储这种类型的对象。这是否意味着我将它们存储在一个平面文件中?是否有某种方法可以将高指定类型的对象与数据库中的字段/列相关联?
When I think about using a Sqlite database, it seems that all of the work will be done in the database calls, and Haskell types will have comparatively little to do with it. This seems bad.
当我考虑使用Sqlite数据库时,似乎所有的工作都将在数据库调用中完成,而Haskell类型则与之相对较少。这似乎是坏的。
In summary, the question is how best can I model the data structure of my simple to do list application in keeping with the notions of functional programming and Haskell ideals that I am seeking to practice with this project?
综上所述,问题是如何根据函数式编程和Haskell理想的概念对简单的do list应用程序的数据结构进行建模,而Haskell理想正是我在这个项目中寻求实现的呢?
2 个解决方案
#1
7
The Show/Read combination is a tremendously easy way of serializing and deserializing the internal state of your application, and because of purity it basically always works too. Furthermore, you'll get good practice writing functions that slice and dice lists, since you'll be able to treat the list as if it is entirely loaded into memory (and maybe if you want to play with some more efficient data structures, you can look at ways to optimize different queries.)
Show/Read组合是对应用程序的内部状态进行序列化和反序列化的一种非常简单的方法,而且由于纯度的原因,它基本上也总是有效的。此外,您将获得编写切片和骰子列表函数的良好实践,因为您将能够将列表视为完全加载到内存中(如果您希望使用一些更高效的数据结构,您可以寻找优化不同查询的方法)。
For example, if I want to find all items that are due before some date, I can write this using a filter on due:
例如,如果我想在某个日期之前找到所有要到期的项目,我可以用一个适当的过滤器来写这个:
dueBefore (ToDoList ts) d = ToDoList (filter (\t -> due t <= d) ts)
Some style nitpicks on your pseudocode:
对伪代码的一些风格吹毛求疵:
-
Since all accessor functions (id, die. created...) get dumped into the module-wide namespace, it's good style to prefix/suffix them with the name of the record, such as todoId, todoDie. In this particular case,
id
is a real function, so you shouldn't shadow it!因为所有的访问函数(id, die)。被转储到模块范围的名称空间中,最好用记录的名称(如todoId、todoDie)给它们加上前缀/后缀。在这种情况下,id是一个实函数,所以不应该对它进行阴影处理!
-
ToDoList is a single constructor datatype with one value; you probably actually just want a newtype or type synonym. (Exercise: rewrite the above snippet to work with a type synonym.)
ToDoList是具有一个值的单个构造函数数据类型;实际上,您可能只想要一个newtype或type synonym。(练习:重写上面的代码片段以使用同义词类型。)
-
You probably want an
Ord
instance on Priority and Status您可能需要优先级和状态的Ord实例。
#2
2
I think that a flat file will make your life easier, and will work just as well, unless you have such a terrible backlog of work that you need a database to hold it all ;P
我认为扁平化文件将使您的生活更容易,并将同样工作,除非您有如此糟糕的工作积压,您需要一个数据库来保存它
I would recomend reading Learn You a Haskell for Great Good, or at least, this chapter. It even contains a short TODO application similar to what you are trying to write.
我想说的是,阅读《学一个Haskell》对你大有裨益,至少这一章是如此。它甚至包含一个简短的TODO应用程序,类似于您正在尝试编写的应用程序。
Now, you should have no problems writing and reading all your values to a file, so long as they all derive Show and Read.
现在,只要它们都派生出Show和Read,您就可以将所有值写入和读取到文件中。
#1
7
The Show/Read combination is a tremendously easy way of serializing and deserializing the internal state of your application, and because of purity it basically always works too. Furthermore, you'll get good practice writing functions that slice and dice lists, since you'll be able to treat the list as if it is entirely loaded into memory (and maybe if you want to play with some more efficient data structures, you can look at ways to optimize different queries.)
Show/Read组合是对应用程序的内部状态进行序列化和反序列化的一种非常简单的方法,而且由于纯度的原因,它基本上也总是有效的。此外,您将获得编写切片和骰子列表函数的良好实践,因为您将能够将列表视为完全加载到内存中(如果您希望使用一些更高效的数据结构,您可以寻找优化不同查询的方法)。
For example, if I want to find all items that are due before some date, I can write this using a filter on due:
例如,如果我想在某个日期之前找到所有要到期的项目,我可以用一个适当的过滤器来写这个:
dueBefore (ToDoList ts) d = ToDoList (filter (\t -> due t <= d) ts)
Some style nitpicks on your pseudocode:
对伪代码的一些风格吹毛求疵:
-
Since all accessor functions (id, die. created...) get dumped into the module-wide namespace, it's good style to prefix/suffix them with the name of the record, such as todoId, todoDie. In this particular case,
id
is a real function, so you shouldn't shadow it!因为所有的访问函数(id, die)。被转储到模块范围的名称空间中,最好用记录的名称(如todoId、todoDie)给它们加上前缀/后缀。在这种情况下,id是一个实函数,所以不应该对它进行阴影处理!
-
ToDoList is a single constructor datatype with one value; you probably actually just want a newtype or type synonym. (Exercise: rewrite the above snippet to work with a type synonym.)
ToDoList是具有一个值的单个构造函数数据类型;实际上,您可能只想要一个newtype或type synonym。(练习:重写上面的代码片段以使用同义词类型。)
-
You probably want an
Ord
instance on Priority and Status您可能需要优先级和状态的Ord实例。
#2
2
I think that a flat file will make your life easier, and will work just as well, unless you have such a terrible backlog of work that you need a database to hold it all ;P
我认为扁平化文件将使您的生活更容易,并将同样工作,除非您有如此糟糕的工作积压,您需要一个数据库来保存它
I would recomend reading Learn You a Haskell for Great Good, or at least, this chapter. It even contains a short TODO application similar to what you are trying to write.
我想说的是,阅读《学一个Haskell》对你大有裨益,至少这一章是如此。它甚至包含一个简短的TODO应用程序,类似于您正在尝试编写的应用程序。
Now, you should have no problems writing and reading all your values to a file, so long as they all derive Show and Read.
现在,只要它们都派生出Show和Read,您就可以将所有值写入和读取到文件中。