[Haskell] Why is newChan in the IO Monad?
Nick Benton
nick at microsoft.com
Fri Apr 23 16:20:31 EDT 2004
Double is a pretty poor choice of "whatever" (just thinking about
equality of floating point numbers puts one in a state of sin :-)).
Worse still, your newChanSafe would be type-unsafe if you gave it that
polymorphic type (think about it).
So let's assume channels aren't polymorphic and that we were were going
to use ints or strings or something as identities. Then since your
newChan has got to be injective, you could just identify chan with int
and be done with it. Even then, there's still quite a lot going on:
Firstly, of course, the equation I wrote *does* actually hold in Haskell
with the current definitions. But it would fail to hold if newchan were
a non-normal form, each evaluation of which returned a new name, which
was what was intended.
Secondly, if you manage your own channel identities you'll end up
carrying around a "current" identity/name supply and having an operation
which takes a supply and gives you a fresh name and a new supply. That's
just another monad (TX = supply->X*supply, gensym : Tname). If the type
you use for names isn't abstract (e.g. you just use int) then the
resulting equational theory will be weak (because contexts can supply
all the names).
A more refined treatment is possible. If (and only if) you make the type
of names and their operations more abstract then there is a very nice
monad just for name generation, which you could separate from the monad
for actually sending and receiving values. One advantage of doing that
is that the dynamic allocation monad satisfies more equations than the
IO monad (it's commutative, for example). Then there's a monad morphism
that lets you lift new name generation into the IO monad when you want
to use the channels (IO is definitely not commutative). If you want to
do fancy program transformations then this extra refinement is helpful.
If you don't, having two monads instead of one just makes life even more
complicated.
Now, giving newChan a non-monadic type amounts to making this new name
monad *implicit* - allowing divergence and/or name-generation to happen
during evaluation without reflecting either possibility in the types. As
I said, this breaks the equational theory of the language. But other
people have taken the position that maybe it doesn't break it all that
badly, and that one can make sure that the compiler's transformations
respect the weaker theory [1]. This is what happens in "observable
sharing", which was suggested precisely because others, like you, found
doing gensym monadically in Haskell to be a bit tedious:
K. Claessen and D. Sands. Observable sharing for functional circuit
description. In ASIAN'99, volume 1742 of Lecture Notes in Computer
Science, pages 62--73. Springer-Verlag, 1999.
Names in circuit descriptions are used to express the sharing which
distinguishes cyclic circuits from infinite ones. In the case of
channels, however, you don't just compare the names for equality - all
non-trivial uses of them are going to involve sends and receives and
hence put you into the IO monad anyway. So messing up the theory of the
whole language just to to have a few fewer IOs doesn't seem a good
tradeoff.
If you're interested in the theory, there's a big (and rapidly growing!)
literature on the semantics of name generation - see the work of Pitts &
Stark on the \nu-calculus, for example.
To summarize the above ramble: it's an excellent question, but you
almost certainly want to leave newChan where it is.
Nick
[1] arguably, including strict constructs (pattern matching, strict
sequencing) is already a step down this road. But the Haskell/Miranda
"war of the bottoms" was all a long time ago...
-----Original Message-----
From: S. Alexander Jacobson [mailto:alex at alexjacobson.com]
Sent: 23 April 2004 20:05
To: Nick Benton
Cc: Haskell Mailing List
Subject: RE: [Haskell] Why is newChan in the IO Monad?
Yes, that makes sense, but I'm ok with passing in
an identity. I'd like a function like this:
newChanSafe::Identity -> Chan a
type Identity = Double -- or whatever
-Alex-
_________________________________________________________________
S. Alexander Jacobson mailto:me at alexjacobson.com
tel:917-770-6565 http://alexjacobson.com
On Fri, 23 Apr 2004, Nick Benton wrote:
> Channels have identity, so allocating a new one is a side effecting
> operation. Having it outside the IO monad would require (for example):
>
> (newChan, newChan) = (let x = newChan in (x,x))
>
> which is wrong. If you wrap newChan in unsafePerformIO then the
compiler
> will feel free to apply rewrites like the above, which is unlikely to
be
> what you wanted.
>
> Nick
> -----Original Message-----
> From: haskell-bounces at haskell.org [mailto:haskell-bounces at haskell.org]
> On Behalf Of S. Alexander Jacobson
> Sent: 23 April 2004 19:22
> To: Haskell Mailing List
> Subject: [Haskell] Why is newChan in the IO Monad?
>
> Nothing actually happens when newChan is called
> except construction of a new datastructure. It
> would be nice to have non IO monad code be able to
> create a new Chan that gets passed to IO code that
> uses it somewhere else.
>
> Alternatively, is there a way to create a Chan
> outside the IO monad?
>
> -Alex-
>
> _________________________________________________________________
> S. Alexander Jacobson mailto:me at alexjacobson.com
> tel:917-770-6565 http://alexjacobson.com
> _______________________________________________
> Haskell mailing list
> Haskell at haskell.org
> http://www.haskell.org/mailman/listinfo/haskell
>
More information about the Haskell
mailing list