RFCs icon indicating copy to clipboard operation
RFCs copied to clipboard

Exporting a `toSeq` overload messes up iterator visibility

Open bluenote10 opened this issue 6 years ago • 10 comments

# file: main.nim
import sequtils
import tables

# this import messes up the functionality of table iterators
import b 

let t = initOrderedTable[int, int]()
let s = toSeq(keys(t))
echo s
# file: b.nim
type
  Data*[T] = object
    data*: seq[T]

proc toSeq*[T](c: Data[T]): seq[T] =
  c.data

Compiling main.nim results in: Error: attempting to call undeclared routine: 'keys'. Basically none of the iterators from tables can be used in a toSeq context when another module (accidentally) exports an overload of toSeq.

bluenote10 avatar Mar 11 '18 09:03 bluenote10

That's because overloaded procs need to have the same untyped parameters in the same positions. Documented limitation. sequtils.toSeq always works then. Don't "accidentically export" a toSeq.

Araq avatar Mar 12 '18 07:03 Araq

Having a toSeq function is the natural choice for any data structure which support "to seq conversion", so this is a rather nasty pitfall. In fact I have been using toSeq functions in several projects of mine and I always though toSeq + iterator visibility is just broken in general because I never could do something like toSeq(key(t)) anywhere. It was only by chance to find the cause...

Wouldn't it be possible to solve the issue by avoiding an untyped implementation of toSeq in sequtils and explicitly implement it for iterators and sized collections (maybe by introducing a concept).

Also I struggle to find the documented limitation and I still can't make sense of the error message here. Apparently the overloading resolution seems to pick the right version of toSeq here, otherwise I would expect a different error. But why does the compiler complain with attempting to call an "undeclared" routine, which obviously is declared?

bluenote10 avatar Mar 12 '18 22:03 bluenote10

Wouldn't it be possible to solve the issue by avoiding an untyped implementation of toSeq in sequtils and explicitly implement it for iterators and sized collections (maybe by introducing a concept).

Certainly.

Apparently the overloading resolution seems to pick the right version of toSeq here, otherwise I would expect a different error. But why does the compiler complain with attempting to call an "undeclared" routine, which obviously is declared?

Because it doesn't pick up the right overload.

Araq avatar Mar 13 '18 01:03 Araq

Certainly.

Then we should probably go for a typed solution in the stdlib to avoid the issue. If you don't mind I'll reopen...

Edit: Oh haha, I cannot re-open :).

bluenote10 avatar Mar 17 '18 19:03 bluenote10

In fact even the standard library violates the "don't accidentally export a toSeq" for instance in nre, i.e. this also fails to compile:

import sequtils, tables, nre

let t = initOrderedTable[int, int]()
let s = toSeq(keys(t))

I'm afraid the (current?) limitations of inline iterators make a proper solution very tough :-(.

bluenote10 avatar Mar 18 '18 17:03 bluenote10

In fact even the standard library violates the "don't accidentally export a toSeq" for instance in nre, i.e. this also fails to compile

I know.

Araq avatar Mar 18 '18 18:03 Araq

just hit this bug again; import nre messes up toSeq.

Wouldn't it be possible to solve the issue by avoiding an untyped implementation of toSeq in sequtils and explicitly implement it for iterators and sized collections (maybe by introducing a concept).

Certainly.

no, unfortunately with the way iterators are implemented in the compiler today, I don't think it's possible to have a toSeq(a: typed). See also https://github.com/nim-lang/Nim/issues/9219

timotheecour avatar Oct 05 '18 23:10 timotheecour

I don't think it's possible to have a toSeq(a: typed)

I think the proposal is to have various overloads of toSeq - one for iterators and the other ones for specific collections. As far as I can tell, this should work, at the cost of some repetition

andreaferretti avatar Oct 08 '18 08:10 andreaferretti

This issue has been automatically marked as stale because it has not had recent activity. If you think it is still a valid issue, write a comment below; otherwise it will be closed. Thank you for your contributions.

stale[bot] avatar Aug 04 '20 07:08 stale[bot]

I think the proposal is to have various overloads of toSeq - one for iterators and the other ones for specific collections. As far as I can tell, this should work, at the cost of some repetition

i don't see how this can work without a compiler change; the problem is the toSeq overload that takes an untyped param to accomodate for any iterable, eg foo(3) below.

just hit this bug again; import nre messes up toSeq.

for reference, the bug is when toSeq requires an untyped param:

when true:
  import sequtils
  # import nre # uncomment would give: Error: attempting to call routine: 'foo'
  iterator foo(n: int): int =
    for i in 0..<n: yield i
  echo toSeq(1..2)
  echo toSeq([1,2])
  echo toSeq(foo(3)) # this is the case broken by `import nre`
  • alias + enableif fixes this, see https://github.com/timotheecour/Nim/issues/322
  • this warning should be made more precise IMO; not all usages of toSeq are broken, only the one involving an untyped iterable

If you love sequtils.toSeq we have bad news for you. This library doesn't work with it due to documented compiler limitations. As a workaround, use this:

timotheecour avatar Dec 21 '20 22:12 timotheecour