1

Chapter 9 of Test-Driven Development with Idris presents the following data type and removeElem function.

import Data.Vect

data MyElem : a -> Vect k a -> Type where
   MyHere  : MyElem x (x :: xs)
   MyThere : (later : MyElem x xs) -> MyElem x (y :: xs)

-- I slightly modified the definition of this function from the text.
removeElem : (value : a) -> (xs : Vect (S n) a) -> (prf : MyElem value xs) -> Vect n a
removeElem value (value :: ys) MyHere         = ys
removeElem value (y :: ys)    (MyThere later) = removeElem value (y :: ys) (MyThere later)

The following works:

*lecture> removeElem 1 [1,2,3] MyHere
[2, 3] : Vect 2 Integer

But, the following call is still running after a few minutes:

*lecture> removeElem 2 [1,2,3] (MyThere MyHere)

Why is this, I'm assuming, compilation so slow?

Cactus
  • 27,075
  • 9
  • 69
  • 149
Kevin Meredith
  • 41,036
  • 63
  • 209
  • 384

1 Answers1

2

The second case of your removeElem reads

removeElem value (y :: ys)    (MyThere later) = removeElem value (y :: ys) (MyThere later)

The right-hand side is exactly the same as the left-hand side; so your recursion diverges. This is why evaluation hangs.

Note that Idris would have caught this error if you declared that removeElem should be total:

total removeElem : (value : a) -> (xs : Vect (S n) a) -> (prf : MyElem value xs) -> Vect n a
removeElem value (value :: ys) MyHere         = ys
removeElem value (y :: ys)    (MyThere later) = removeElem value (y :: ys) (MyThere later)

which results in the compile-time error

RemoveElem.idr line 9 col 0:

Main.removeElem is possibly not total due to recursive path Main.removeElem

Cactus
  • 27,075
  • 9
  • 69
  • 149
  • I realize there's an obvious follow-up question in "how would I fix `removeElem`'s definition", but since you are studying, I assume you want to try and figure it out yourself first. – Cactus Jun 21 '17 at 01:55
  • Thanks. What does "unfounded" mean? – Kevin Meredith Jun 21 '17 at 02:17
  • I originally meant that the recursion is not well-founded. But now that I think about it, maybe that's the wrong terminology to use here... – Cactus Jun 21 '17 at 03:24
  • I've updated my answer slightly to make it hopefully clearer. – Cactus Jun 21 '17 at 03:25
  • It seems to me that it'd be a good idea to always put `total` in front of the function's signature. Under what circumstances would you not use `total`, Cactus? – Kevin Meredith Jun 21 '17 at 10:33
  • 1
    You can make `total` the default by adding `%default total` to your module (and then you need to mark your partial functions with `partial`). As for your question, you'd not use `total` if you are defining a non-total function, of course :) [The Idris book](https://www.manning.com/books/type-driven-development-with-idris) has some nice examples of programs that are all `total` except for a single `partial` definition to allow e.g. a server to keep accepting new requests indefinitely. The idea is that the `total`ity of all other functions ensures that any given request is handled in finite time – Cactus Jun 22 '17 at 01:26
  • The consensus with a coworker of mine is that indeed it is the correct terminology that your recursion (in the original question) was not well-founded. – Cactus Jun 22 '17 at 01:27