[Haskell-beginners] "downcasting"

Andres Löh andres at well-typed.com
Thu Oct 25 00:15:39 CEST 2012

Hi Emmanuel.

It's somewhat confusing that in Haskell, data constructors and
datatypes can have the same name. They're nevertheless different
beasts. A data constructor is a way to construct values of a datatype,
or to destruct them via pattern matching. Such constructors themselves
are *not* types.

The definition of JSValue looks as follows:

> data JSValue
>     = JSNull
>     | JSBool     !Bool
>     | JSRational Bool{-as Float?-} !Rational
>     | JSString   JSString
>     | JSArray    [JSValue]
>     | JSObject   (JSObject JSValue)
>     deriving (Show, Read, Eq, Ord, Typeable)

So there are six different ways to construct a JSValue, the last one
is the JSObject constructor. It contains one item which is of *type*
JSObject JSValue. This time, JSObject refers to a datatype, also
defined in the library:

> newtype JSObject e = JSONObject { fromJSObject :: [(String, e)] }
>     deriving (Eq, Ord, Show, Read, Typeable )

Now to your functions: if you write

> getObject :: JSValue -> JSObject JSValue
> getObject x@(JSObject _) = x


> getObject :: JSValue -> JSObject JSValue
> getObject (JSObject x) = JSObject x

you are writing essentially the identity function, but trying to
assign a more specific type to the value. This doesn't quite work in
Haskell. There's no subtyping between different datatypes.

Instead, what you should do is perform the pattern match once and
extract its contents:

> getObject :: JSValue -> JSObject JSValue
> getObject (JSObject x) = x

Now you have the stuff that was "inside" the constructor, and isolated
the point of failure.


Andres Löh, Haskell Consultant
Well-Typed LLP, http://www.well-typed.com

More information about the Beginners mailing list