The madness of implicit parameters: cured?

Ben Rudiak-Gould benrg@dark.darkweb.com
Sun, 3 Aug 2003 08:01:52 -0700 (PDT)


On Sat, 2 Aug 2003, Derek Elkins wrote:

> Ben Rudiak-Gould <benrg@dark.darkweb.com> wrote:
> 
> > More recently, I've realized that I really don't understand implicit
> > parameters at all. They seemed simple enough at first, but when I look
> > at an expression like
> > 
> >     f x = let g y = ?foo in g
> > 
> > I realize that I have no idea what f's type should be.
> 
> Do you have problems finding the type of
> 
> f x = let g y = 4 in g
> ?
> it works -EXACTLY- the same way.

No, there is a big difference: There's just one dictionary value for each
type class instance, and it's global to the whole application. As a
result, it doesn't matter when hidden dictionary arguments are applied.
But implicit parameters are scoped, so it matters a lot when they're
applied. This opens up a big can of worms that the type-context system has
never had to deal with before.


> Implicit parameters aren't as "flexible" as full dynamic scoping would
> be.  For example,
> 
> f g = let ?foo = 5 in g ()
> g x = ?foo
> 
> f g :: {foo :: a) => a 
> NOT
> f g :: Num a => a
> i.e. it doesn't evaluate to 5.

But it should. Or rather, either it should evaluate to 5 or it should be a
compile-time error, because the programmer clearly thought that g within
the body of f had a implicit parameter ?foo when in fact it didn't.

In my proposal this is a type error; the message would be something like
"g () does not have an implicit parameter ?foo, in the expression 'let
?foo = 5 in g ()'". It would be easy to add a helpful message to the
effect that function arguments can't have implicit parameters (which is
still true in my proposal).


> > The final straw was:
> > 
> >     Prelude> let ?x = 1 in let g = ?x in let ?x = 2 in g
> >     1
> >     Prelude> let ?x = 1 in let g () = ?x in let ?x = 2 in g ()
> >     2
> 
> Compare,
> 
> let g = (<) in (g 'a' 'b',g 1 2)
> 
> let g x y = x < y in (g 'a' 'b',g 1 2)

One of your expressions behaves as expected, the other is a type error.
I'm happy with both of these outcomes. But my expressions both typecheck,
but then do different things. That's scary. I was burned by this once, and
now when I try to write code with implicit parameters I have to think to
myself constantly, "is this going to do what I expect? Is this going to do
what I expect?". The big selling point of functional programming is that
it makes programs easy to reason about, but implicit parameters (as
presently implemented) violate this principle to an extent that I've never
seen in any other language except Perl.


> In fact, if you use -fno-monomorphism-restriction, your examples above
> give you the same numbers.

I should point out that this is the only case in which the presence or
absence of the monomorphism restriction changes the meaning of a correct
program. In other words, it's not a restriction at all here, but an
interpretation. I hope that worries you at least a little bit.

My proposal does not behave that way: at worst, turning on the
monomorphism restriction creates an error where there was none before,
just as in the case of type class constraints.


-- Ben