The madness of implicit parameters: cured?

Derek Elkins ddarius@hotpop.com
Sun, 3 Aug 2003 11:52:02 -0400


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

> 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.

I wasn't talking about the semantics, I was talking about the
type-inference.

The "Implicit Parameters: dynamic scoping and static typing" makes a
point at least twice that the interpretation of code using implicit
parameters is dependent on the type not just the syntax.

> > 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.

How does g not have an implicit parameter.  It could not but that's not
the issue here.  The issue is the programmer thought that the binding to
?foo would influence g.  Here the type would make it obvious that it
shouldn't.  Whether you want this ability or not in this or the other
example is debatable.

> 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).

Would let ?foo = 4 = id be an error then?  or let ?foo = 4 in 4?  There
is no reason to require the body to use an implicit
parameter.

> > > 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?". 

Indeed, this is pretty bad, but except for the monomorphism restriction
issue, the behavior doesn't seem any less comprehensible than dynamic
scoping ever is. However, what to expect is pretty obvious when looking
at the type(at least as far as dynamic scoping is obvious.) The
documentation should mention this behavior or implicit parameters should
not suffer the monomorphism restriction and the documentation should
warn against the potential lack of sharing.

> 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.

Implicit parameters are meant to implement dynamic scoping.  Dynamic
scoping does typically make programs more difficult to reason about.  I
can only think of a few cases where implicit parameters might be a good
idea.  Perhaps I should see what Ashley Yakeley is doing with them as I
virtually never use them.  I typically use a Reader monad whenever I
want behavior like this.

> > 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.

I think, the authors of the implicit parameters paper were in favor of
not having the monomorphism restriction here.  I'm surprised this isn't
documented in the GHC User-guide (an example would also be nice.)  I
would prefer that the behavior was independent of
-fno-monomorphism-restriction, either through (yet another) flag, or by
fixing one choice or the other.  I'd prefer a flag, but if it were
fixed, I'd be in favor of the -fno-monomorphism-restriction
interpretation.