Managing invariants in Clojure / Haskell

I have been comparing OOP and FP methodologies and I could not put my head around one thing in functional programming - keeping invariants in the data structure.

For example imagine the following requirements.

We have a list of projects and every project a has list of tasks and a list of assigned members. Every task can have a worker assigned to it but only from the list of project assigned members.

I can imagine how I can solve this problem in a OOP language, for example in Java, by adding required checks and exceptions, and this would lead in my opinion to more robust code.

But as the data is separated from the behaviour in FP, how should I solve the same problem in FP, say in Clojure or Haskell?


Your question is very general, a lot of strategies could be used to solve related problems but, essentially, checking invariants (at runtime) is "equal" than in OOP

assign :: Task -> Worker -> Either String Task
assign task worker =
    if not (taskProject task) `containsWorker` worker
        then Left "You can't assign..."
        else Right $ task { taskWorkers = worker : taskWorkers task }

One common behavior is hide data constructor (as Task , Worker and Project ), the OOP counterpart is write private constructors.

module Scheduler (
  Task   -- instead `Task (..)`
, Worker -- instead `Worker (..)`
...
, assign
, createTask
, createWorker
...
) where

(I unkown the current Haskell support to friend , protected , ... counterpart probably not exists and you can find a lot of Haskell modules with Some.Module.Internals.Something with private objects)

The main question is about how to structure the whole program to achieve the required behavior.

Real World Haskell is a good starting point to learn about that or as suggested on related question Large-scale design in Haskell?

On the other hand, about pre/post condition in Haskell you can read What are the options for precondition checking in Haskell.


In Clojure it is possible to specify arbitrary :pre and :post conditions on any function. Here's an example from the documentation:

(defn constrained-sqr [x]
    {:pre  [(pos? x)]
     :post [(> % 16), (< % 225)]}
    (* x x))

There's also a very interesting library core.contracts that implements contracts in Clojure.


You say data is "separated" from behavior in an FP language, but in Haskell (I don't know Clojure) you can quite easily define a data structure in a module, make its definition private, and only export functions to manipulate the data.

In other words, Haskell doesn't have (OO-style) classes, but it still has encapsulation.

链接地址: http://www.djcxy.com/p/42878.html

上一篇: Haskell风格和类型设计(应该最小化类型类?)

下一篇: 在Clojure / Haskell中管理不变量