I was browsing the FileStore code and got to thinking that the FileStore data type rubbed me the wrong way because it hides IO, diluting the Purity of Essence of haskell’s precious type system.
In ghci of our code that uses FileStore:
:t darcsFileStore "/home/thartman/patchwiki/wikis/happstacktest/"
darcsFileStore "/home/thartman/patchwiki/wikis/happstacktest/" :: FileStore
I see that and I think, cool, a pure value.
Then I think — oops, wrong.
data FileStore
= FileStore {initialize :: IO (),
save :: forall a.
(Contents a) =>
ResourceName -> Author -> Description -> a -> IO (),
retrieve :: forall a.
(Contents a) =>
ResourceName -> Maybe RevisionId -> IO a,
delete :: ResourceName -> Author -> Description -> IO (),
rename :: ResourceName
-> ResourceName
-> Author
-> Description
-> IO (),
history :: [ResourceName] -> TimeRange -> IO [Revision],
latest :: ResourceName -> IO RevisionId,
revision :: RevisionId -> IO Revision,
index :: IO [ResourceName],
idsMatch :: RevisionId -> RevisionId -> Bool,
search :: SearchQuery -> IO [SearchMatch]}
Now I think, aha, this is OO in haskell.
So, what would I want instead?
First idea: how about something like
type FileStore = [resourceName] -- pure
and then a class of RepoAble or something like that which has a bunch of pure methods that must be implemented for any concrete instance of the class. (revision, latest, retrieve, history, index, idsMatch, search)
Then you would have some unavoidably impure methods like save, delete, rename.
Concrete instances could then be defined for Darcs, Git, Svn, etc. You basically have a nice wrapper over various types of repos.
On second thought though, maybe not such a good idea. To have everything work purely after an initial fetch, the FileStore value would have to contain everything in the repo. Nah.
I just want that darned FileStore value tagged with the IO type somehow so I can tell visually when looking at the code that we’re in impure land.
This is when I realized my mistake.
Since all the fields in FileStore are functions, there’s nothing impure about it after all. If one of the fields of fileStore had a concrete value related to a repo, this would smell wrong, because we’re mixing pure and impure — but that’s not the case here.
I think the confusion for me is that I am used to seeing “interfaces” in haskell defined in terms of implementation of a type class, but here the interface is just the arbitrary fields of a datatype, which I think is a bit less type safe.
So, I still think FileStore could be improved that way by taking advantage of typeclassing, but it’s not a matter of it being impure like I initially thought.
The irony, of course, is that filestore originally used typeclasses instead of records. (And before that, records…)
I forget why we changed. You’d have to ask John, he did the Wed Jan 14 12:00:43 EST 2009 patch that switched from typeclasses to records.
jgm responded at
http://groups.google.com/group/gitit-discuss/msg/6d5c5afc5d334b52