Is there any mechanism in Haskell to do the same thing in record types?
What you can do is hide the constructor, and provide a function as constructor instead.
Say for instance we have a list we want to update, together with a revision number, then we can define it as:
data RevisionList a = RevisionList { theList :: [a],
revision :: Int }
deriving Show
Now we can define a function that initializes the BuildList with an initial list:
revisionList :: [a] -> RevisionList a
revisionList xs = RevisionList { theList = xs, revision=0 }
and by hiding the constructor in the module export, we thus hide the possibility to initialize it with another revision than revision 0. So the module could look like:
module Foo(RevisionList(), revisionList)
data RevisionList a = RevisionList { theList :: [a],
revision :: Int }
revisionList :: [a] -> RevisionList a
revisionList xs = RevisionList { theList = xs, revision=0 }
something like the builder pattern from OOP?
We can for instance use a State monad for that. For instance:
module Foo(RevisionList(), revisionList,
increvision, RevisionListBuilder, prefixList)
import Control.Monad.State.Lazy
type RevisionListBuilder a = State (RevisionList a)
increvision :: RevisionListBuilder a ()
increvision = do
rl <- get
put (rl { revision = 1 + revision rl})
prefixList :: a -> RevisionListBuilder a ()
prefixList x = do
rl <- get
put (rl { theList = x : theList rl })
increvision
So we get the RevisionList thus far, perform updates, put the new result back, and increment the revision number.
So now another module can import our Foo, and use the builder like:
some_building :: RevisionListBuilder Int ()
some_building = do
prefixList 4
prefixList 1
and now we can "make" a RevisionList at revision 2 with as final list [1,4,2,5] with:
import Control.Monad.State.Lazy(execState)
some_rev_list :: RevisionList Int
some_rev_list = execState some_building (revisionList [2,5])
So it would look approximately like:
Foo.hs:
module Foo(RevisionList(), revisionList,
increvision, RevisionListBuilder, prefixList)
data RevisionList a = RevisionList { theList :: [a],
revision :: Int }
deriving Show
type RevisionListBuilder a = State (RevisionList a)
revisionList :: [a] -> RevisionList a
revisionList xs = RevisionList { theList = xs, revision=0 }
increvision :: RevisionListBuilder a ()
increvision = do
rl <- get
put (rl { revision = 1 + revision rl})
prefixList :: a -> RevisionListBuilder a ()
prefixList x = do
rl <- get
put (rl { theList = x : theList rl })
increvision
Bar.hs:
import Foo
import Control.Monad.State.Lazy(execState)
some_building :: RevisionListBuilder Int ()
some_building = do
prefixList 4
prefixList 1
some_rev_list :: RevisionList Int
some_rev_list = execState some_building (revisionList [2,5])
So now we have constructed a some_rev_list with the "building" of some_building:
Foo Bar> some_rev_list
RevisionList {theList = [1,4,2,5], revision = 2}