read only constructors
when you export a data constructor, you export not only the ability to pattern match with said constructor, but also to create new values with that constructor. This can be harmful to abstract data types that want to make use of memoizing constructor or enforce invarients on a data type with constructor functions.
An example would be
data Term = LetRec {
defns :: [(Name,Term)],
body :: Term,
freeVars :: [Name],
sccDefns :: [[(Name,Term)]] }
| Ap Term Term
| Case ...
now, calculating the freevars and the strongly connected components are expensive operations so we would like them to be memoized in the constructor and only created if they are actually needed but also shared if they are needed again for the same type.
we can create a constructor to enforce this like so
letRec :: [(Name,Term)] -> Term -> Term
letRec defns body = LetRec {
defns = defns,
body = body,
freeVars = fv body,
sccDefns = calculateScc defns }
and that is good, but we cannot prevent people from getting around our constructor function without also blocking the very useful ability to pattern match on Terms.
proposal
allow data consructors to be exported and imported readonly
module Foo(bar,Foo(readonly Bar, Baz)) where
data Foo = Bar Int Int | Baz
-- enforce invarient that bars
-- second argument is always
-- twice the first one
bar i = Bar i (i*2)
we might want to reused the 'closed' keyword from the ClosedClasses proposal. see also the abstraction section in ExistingRecords.
Comment
This is related to Views and PatternSynonyms. There is definitely scope for improvement in this area, but somehow I think there should be a unified, more comprehensive story. This seems, to me, to be too much of a stop-gap measure.