Hi

I hope you don't mind me copying this reply to the mailing list, as it may be of more general interest.

Jonathan Lee wrote:
I've been trying the tutorial and I'm coming to the part about the
(vec x) function.  I'm a bit confused on how the implicit syntax as
when I input:

    ( n : Nat ;  x : X  !
let  !--------------------!
    ! vec_n x : Vec n X )

Mea culpa. Since writing the tutorial, I broke this example (and one or two others) when I made a change to the system's default strategy for ordering implicit arguments relative to each other and to the explicit arguments. You can see the symptoms in the Horace buffer

---------------------------------------------------------------
( n : Nat ; x : X ! let !--------------------! ; vec _ n x [] ! vec _n x : Vec n X ) ---------------------------------------------------------------

{Undo} {Reset} {Quit} n : * ; n' : Nat ; x : n |- vec _ n x : Vec n' n {Elaborate} Epigram has decided, come what may, that the type (which you've called X) will be the first implicit argument. It has taken your advice to call the first implicit argument n, even though it's not the argument you wanted to put there. It's clearly wrong: you're clearly asking for the number to be implicit and to stand first. It's a case of fix-one-bug-cause-another, compounded by poor maintenance of the accompanying collection of examples and documents.

When the tutorial was written, implicit arguments were inserted just where they were needed. The above declaration would have given you the order vec_n_X x. If you'd left off the n : Nat in the premises, you'd have got vec_X x _n, hence the premise for n in the first place.

These days, the system inserts implicit arguments as far left as possible, relative to the explicitly declared arguments, so you get vcons_X_n_x, because it *can* put X first. In the absence of any explicitly underscored arguments, this heuristic is usually cleaner, but you should still be able to override it with a declaration like the one you tried.

This sort of issue comes up any time you have these weird functions like vec which need to *use* an inferrable argument. When you use vec, it's usually ok to allow the result type to determine the length, but in vec's definition you need to make reference to the length. Correspondingly, you need to use the above form, with its broken implementation. You can play safe by fixing the order of arguments in full.

---------------------------------------------------------------
( n ; X ; x : X ! let !--------------------! ! vec _n x : Vec n X ) vec _ n x <= rec n { vec _ n x <= case n { vec _ zero x => vnil vec _ (suc n) x => vcons x (vec x) } } ---------------------------------------------------------------


Nasty, but it leaves nothing to chance.

What ought to happen, even in Epigram 1? Implicit arguments should be quantified as far left as is possible according to dependency and consistent with the form which the programmer has written. When you write f_i, you close off the possibility of other implicit arguments before i. To my mind, the program you have written does indeed determine the order n, X, x, and the fact that you don't get it is a *bug*. Meanwhile, if you write

 x : X ;  xs : Vec n X
------------------------------
 vcons x xs : Vec (suc n) X

you should be guaranteed to have n and X implicitly at the front (ie either vcons_n_X x xs or vcons_X_n x xs) but in no particular order. (The current implementation gives you vcons_X_n x xs; the old version gave you vcons_X x _n xs, which is really bad.)

What are the chances that I'll fix this bug? Slim but non-zero. On the one hand, it causes confusion. On the other hand, there's a reliable workaround. I certainly know whre the bug is to be found, but I worry that fiddling with it might have unintended consequences. I'm also don't know when I'd find the time.

I hope this is a suitably comprehensive and candid, if less than satisfactory answer to your entirely sensible question.

All the best

Conor

Reply via email to