getting the right types

Simon Peyton-Jones simonpj@microsoft.com
Tue, 5 Nov 2002 11:04:24 -0000


Interesting.  You want to control a data type used *inside* an algorithm
from outside.  Here's a smaller version:

	foo :: String -> String
	foo s =3D show (read s)

This is ambiguous; what type should 'read' return?  Your idea: pass in
that type via a proxy value:

	foo :: (Read a, Show a) =3D> a -> String -> String
	foo p s =3D show (read s `asTypeOf` p)

Now you can say

	foo (undefined :: Int) "34"

and it'll work.  A neater way to do this with GHC is to use scoped type
variables:

	foo :: (Read a, Show a) =3D> a -> String -> String
	foo (p::a) s =3D show (read s :: a)


One reason this is neater is that it scales up better when you want to
pass a type constructor instead of a type.   First define a data type
with no constructors, and a phantom type arg

	data Proxy (a :: * -> *)

Now you can write a function like this

	f :: (MArray a ix e) =3D> Proxy a -> ....
	f (p :: Proxy a) ... =3D do { (arr :: a ix e) <- newArray ...

The type variable 'a' is bound by the (Proxy a) pattern, and scopes over
the body. The
type variables 'ix' and 'e' in the do-binding are bound right there
(since they aren't already in scope).

Another use for phantom types!

Simon


| -----Original Message-----
| From: Hal Daume III [mailto:hdaume@ISI.EDU]
| Sent: 03 November 2002 19:31
| To: Haskell Mailing List
| Subject: getting the right types
|=20
| Hi all,
|=20
| I have a function which essentially looks like this:
|=20
|   f my_data =3D do
|     a1 <- newArray ...
|     ...
|     a2 <- newArray ...
|     g my_data a1 a2
|=20
| where f is a monadic operation essentially of type 'a -> m a'.  The
| problem is that when this function isn't given a type signature, you
get
| something like:
|=20
|   f :: (MArray a1 p e, MArray a2 p e) =3D> t
|=20
| where a1 and a2 aren't bound in t.  Now, I could provide a type
signature
| on the array expressions, something like:
|=20
|     ... (a1 :: IOArray Int Int) <- newArray ...
|=20
| but i would like to be able to use unboxed arrays when possible, and i
| don't want to bind the funtion to be in IO.  My current solution is to
| define:
|=20
|   data ArrayType arr prob =3D forall ix . ArrayType (arr ix prob)
|   asArray :: arr ix prob -> ArrayType arr prob -> arr ix prob
|   a `asArray` _ =3D a
|=20
| Sort of like asTypeOf.  Then, I change f to:
|=20
|   f :: (MArray arr p e) =3D> ArrayType arr p -> t -> m t
|   f arr_type ... =3D ...
|=20
| And inside f I have a function:
|=20
|   newArray_arr bnds init =3D
|       do a <- newArray bnds init
|          return (a `asArray` arr_type)
|=20
| This enables me to create arrays of arbitrary representation with the
same
| values but different indices (which is important).
|=20
| However, this strikes me as horribly hackish.  Is there some better
way to
| do this?
|=20
|  - Hal
|=20
| --
| Hal Daume III
|=20
|  "Computer science is no more about computers    | hdaume@isi.edu
|   than astronomy is about telescopes." -Dijkstra | www.isi.edu/~hdaume
|=20
| _______________________________________________
| Haskell mailing list
| Haskell@haskell.org
| http://www.haskell.org/mailman/listinfo/haskell