This is a good model for object oriented languages, but it is the well-known Haskell antipattern . Read this article. I want you to read this most of all what I say.
See also this answer , but on the condition that I believe that GADTs are more elegant than existential types (see below).
Please try to find the best functional ways to program your program, rather than the best functional programming options for reintroducing object-oriented programming. You still haven't made any purpose for your code other than your hope for OO-style programming.
(Imagine Craig, a C programmer, new to Java, who is trying to find how to make a pointer to an array of structures or get stuck trying to make a method with malloc functionality, or get frustrated because there is no Arithmetic pointer. Janet, a Java programmer, answered would you ask Craig why he wants to make pointers himself, whatβs wrong with garbage collection, and why would anyone ever want pointer arithmetic when they have arrays with built-in constraints? Indeed Craig would be better off learning to program go Automatic Java first, before deciding what C functions it really cannot do, OO is a different paradigm for C-people as close as possible to a machine, while a relatively-machine-independent philosophy still exists. Craig must learn a new paradigm in as his first priority, which could make him a better C. programmer. Don't visit France to speak only English, watch CNN and have McDonalds! There's a point in which you should be trying to write your code in the most functional way possible.)
If you really want to have other information about your data, except that it is subject to your contract, one of the ways is to use GADT. You should know that Haskell will guide you to your claim; there is no casting to get you out of a mindless design decision. (Casting is a way to turn compile-time checks into runtime checks or, in other words, a way to turn compile-time errors into run-time errors. I don't see anything good about it.)
{-
Now that you have encapsulated your data, you can do whatever you like, as if it were a regular data type, and restore any of the following Contract ed functions:
munged :: Encapsulated -> String munged (Encapsulate a) = toString.alter.alter.alter $ a
If you like it so much, you can store a whole bunch of Encapsulated data on the map, there is no need to do anything special or redefine Data.Map for your existential, because there is a powerful functional paradigm: Data.Map makes no assumptions about your data. This is parametric polymorphism and works for anything. Anything, even functions. The only thing it does is that your keys are sorted ( Ord k => ) and your data is homogeneous (and our GADT is homogeneous, despite the fact that it is made from heterogeneous data).
This is one way to do what you asked for, but if we knew what you wanted it for, we could give you the best advice. (Perhaps another new question!) Please really read the article I am involved with and see if you can implement your class as a data type full of functions / results, and your instances as functions for this data type.