5

I'm trying to create a proc that returns a custom tuple that contains a single element that is a proc type i.e.

type
  CustomTuple = tuple
    foo: proc(input: int): int

proc createCustomTuple(): CustomTuple =
  (foo: proc(input: int): int = 10)

However, when I compile this I get the following error (I am compiling with Nim Version 1.2.6 on Windows).

Error: type mismatch: got <tuple[foo: proc (input: int): int{.noSideEffect, gcsafe, locks: 0.}]> but expected 'CustomTuple = tuple[foo: proc (input: int): int{.closure.}]'

So the compiler thinks I am returning a regular tuple and not a CustomTuple but I have no idea how to change this to make it work. The documentation for tuples in the Nim manual show custom tuples being constructed in the way that I am doing it and I couldn't find any examples of returning a custom tuple from a proc.

If I change my CustomTuple definition to contain types that aren't procs then it compiles successfully so it appears it has something to do my custom tuple containing a proc that is causing this to fail to compile.

Can anyone explain why the above code is not compiling?

Benjamin Gale
  • 12,977
  • 6
  • 62
  • 100

2 Answers2

4

I think the reason my original code isn't working is because Nim is unable to convert the proc to a closure automatically when it is added to a tuple. There is some discussion around this in the Nim forums.

As the proc is not converted to a closure the compiler is unable to determine that the tuple being returned is a CustomTuple because the types don't match which explains the error message.

So when packing a proc in a tuple you need to explicitly convert it to a closure. This can be done by explicitly casting the proc like this:

type
  CustomTuple = tuple
    foo: proc(input: int): int

proc createCustomTuple(): CustomTuple =
  (foo: (proc(input:int):int)(proc(input: int): int = 10))

or by adding a {.closure.} pragma like this (which I think is much cleaner).

type
  CustomTuple = tuple
    foo: proc(input: int): int

proc createCustomTuple(): CustomTuple =
  (foo: proc(input: int): int {.closure.} = 10)

If you don't want to do either of these you can assign the proc to the foo property of the implicit result variable as per Salewski's answer.

This also explains why Salewski's original solution of assigning the proc to the foo property of the implicit result variable worked; the compiler is able to automatically convert the proc to a closure in that case.

Benjamin Gale
  • 12,977
  • 6
  • 62
  • 100
1
type
  CustomObject = object
    foo: proc(input: int): int

proc createCustomObject(): CustomObject =
  #(foo: proc(input: int): int = 10)
  #result.foo = proc(input: int): int = 10
  CustomObject(foo: proc(input: int): int = input * 2)

var x = createCustomObject()
echo x.foo(2)
Salewski
  • 164
  • 1
  • 2
  • +1 This provides a solution that is functionally equivalent but doesn't actually explain why my original code fails to compile which is what I'm really interested in understanding. – Benjamin Gale Aug 29 '20 at 08:27
  • Note that this solution also doesn't require us to say `result.foo = ...` which just highlights that there is something different going on when using custom tuples. – Benjamin Gale Aug 29 '20 at 08:32