# [Haskell-cafe] is 256M RAM insufficient for a 20 millionelementInt/Int map?

Dan Doel dan.doel at gmail.com
Sun Oct 19 12:57:20 EDT 2008

```On Sunday 19 October 2008 10:32:08 am Claus Reinke wrote:
> >> (hint to ghc hackers: 'Data.Map.Map Int !Int' and '[!a]' would really be
> >> useful!-),
> >
> > I can't figure out what that means though.  Strictness is not a
> > property of types or of values, it is a property of functions.   [!]
> > is not a subtype of [] ; IOW, there is no a such that [a] = [!Int]
> > (where [!Int] is a list with strict values).  For example, if we
> > allowed this, the following property breaks:
> >
> >  length xs == length (map f xs)
> >
> > Since it is not true on strict lists.
>
> I'm not entirely sure this couldn't be worked out. Let's say that
> every type breaks into terminating and non-terminating computations:
>
>     a = !a | ^a
>
> For your equation to break, you're assuming implicit conversions
> between some 'a' and '!a', ie, something like
>
>     (undefined :: a) :: !a
>     (const undefined :: a -> b) :: !a -> !a
>
> But those shouldn't typecheck! We can't decide termination, so not
> all objects of type 'a' can be classified into the subtypes '!a' or '^a'.
> Membership in '!a' can be constructive only, and map for strict lists
> would have a type somewhat like this
>
>     mapS :: !(!a -> !a) -> [!a] -> [!a]
>
> Without a function, 'mapS' can't construct an element-strict result list,
> hence the first '!' (and 'mapS undefined' won't typecheck). Nor can it
> do so without a function that can construct (or pass on) strict elements,
> hence the '!' on the result type of the parameter function (so 'mapS
> (const undefined)' won't typecheck, either). Since there are no ways
> of producing an arbitrary '!a' out of thin air, polymorphic parameter
> functions will have type '!a->!a' (if 'a' gets specialised, eg to 'Int',
> then trivial 'Int->!Int' functions are possible).
>
> Being conjured out of thin air, none of this might not hold up under
> closer scrutiny, but papers like [1] suggest that it isn't entirely out of
> reach  - further references or counterexamples appreciated!-)

I don't think there's any realistic way to keep his equation from breaking.
For instance, you might be able to prevent someone from writing:

f :: !a -> !a
f _ = undefined

However, that still doesn't guarantee that you can't write non-terminating
functions, which are semantically bottom:

f :: !a -> !a
f a = f a

Note, that you can even write non-termination for unboxed types:

n :: Int#
n = n

Even though such values may be supposedly unlifted. So, given that second f,
clearly length (map f xs) = _|_ when length xs > 1.

Total languages go through a great deal of effort to prevent this sort of
thing. For instance, many use syntactic checks on the functions your write to
ensure that they are structurally recursive. So, perhaps you could add these
checks to a Haskell compiler, but you'd also have to ensure that you don't
produce total values from non-total values, because doing structural recursion
over coinductive values (like infinite lists) introduces bottom.

And finally, you'd have to do similar checks on Haskell's data types, because
the unrestricted recursive types that Haskell has allow you to introduce
bottom without explicit recursion:

newtype Wrap a = Roll { unRoll :: Wrap a -> a }

-- (\x -> x x) (\x -> x x)
omega :: a
omega = w (Roll w) where w x = unRoll x x

The solution most total languages take is to make sure that all such recursive
types are strictly positive (recursive uses of the type may not be to the left
of an arrow; Wrap clearly fails this check).

Anyhow, it seems to me that to adequately separate all this stuff, and prevent
things like 'length xs = length (mapS f xs)' from breaking, you'd end up with
an entirely separate total sublanguage that doesn't interact much with the
existing part (which kind of ruins the appeal of introducing such annotations
in the first place). Perhaps I'm missing something, though.

Cheers,
Dan
```

More information about the Haskell-Cafe mailing list